Skip to content
Snippets Groups Projects
Commit 57097978 authored by Peter Gerwinski's avatar Peter Gerwinski
Browse files

Musterlösung zu den Übungsaufgaben vom 23.1.2017

parent b26a9c2b
No related branches found
No related tags found
No related merge requests found
File added
% hp-musterloesung-20170123.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017 Peter Gerwinski
%
% This document is free software: you can redistribute it and/or
% modify it either under the terms of the Creative Commons
% Attribution-ShareAlike 3.0 License, or under the terms of the
% GNU General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option)
% any later version.
%
% This document is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this document. If not, see <http://www.gnu.org/licenses/>.
%
% You should have received a copy of the Creative Commons
% Attribution-ShareAlike 3.0 Unported License along with this
% document. If not, see <http://creativecommons.org/licenses/>.
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{ifthen}
\usepackage{gensymb}
\usepackage{sfmath}
\usepackage{enumerate}
\usepackage{tikz}
\newcounter{exercise}
\newcommand{\exercise}[1]{\addtocounter{exercise}{1}\subsection*{Aufgabe \arabic{exercise}: #1}}
\newcommand{\solution}{\goodbreak\subsubsection*{Lösung}}
\newcounter{points}
\newcommand{\points}[1]{\ifthenelse{#1=1}{(1 Punkt)}{(#1 Punkte)}\addtocounter{points}{#1}}
\newcommand{\ItwoC}{I\raisebox{0.5ex}{\footnotesize 2}C}
\newcommand{\ITWOC}{I\raisebox{0.5ex}{\normalsize 2}C}
\newcommand{\gitfile}[2]{\href{https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/#1/#2}{\file{#2}}}
\begin{document}
% \thispagestyle{empty}
\section*{Hardwarenahe Programmierung / Angewandte Informatik\\
Musterlösung zu den Übungsaufgaben -- 23.\ Januar 2017}
\exercise{Ternärer Baum}
Der in der Vorlesung vorgestellte \newterm{binäre Baum\/}
ist nur ein Spezialfall;
im allgemeinen können Bäume auch mehr als zwei Verzweigungen
pro Knotenpunkt haben.
Dies ist nützlich bei der Konstruktion \emph{balancierter Bäume},
also solcher, die auch im \emph{Worst Case\/}
nicht zu einer linearen Liste entarten,
sondern stets eine -- möglichst flache -- Baumstruktur behalten.
Wir betrachten einen Baum mit bis zu drei Verzweigungen pro Knotenpunkt,
einen sog.\ \newterm{ternären Baum}.
Jeder Knoten enthält dann nicht nur einen,
sondern \emph{zwei\/} Werte als Inhalt:
\begin{lstlisting}
typedef struct node
{
int content_left, content_right;
struct node *left, *middle, *right;
} node;
\end{lstlisting}
Wir konstruieren nun einen Baum nach folgenden Regeln:
\vspace{-\medskipamount}
\begin{itemize}\itemsep0pt
\item
Innerhalb eines Knotens sind die Werte sortiert:
\lstinline{content_left} muß stets kleiner sein als \lstinline{content_right}.
\item
Der Zeiger \lstinline{left} zeigt auf Knoten,
deren enthaltene Werte durchweg kleiner sind als \lstinline{content_left}.
\item
Der Zeiger \lstinline{right} zeigt auf Knoten,
deren enthaltene Werte durchweg größer sind als \lstinline{content_right}.
\item
Der Zeiger \lstinline{middle} zeigt auf Knoten,
deren enthaltene Werte durchweg größer sind als \lstinline{content_left},
aber kleiner als \lstinline{content_right}.
\item
Ein Knoten muß nicht immer mit zwei Werten voll besetzt sein;
er darf auch \emph{nur einen\/} gültigen Wert enthalten.
Der Einfachheit halber lassen wir in diesem Beispiel
nur positive Zahlen als Werte zu.
Wenn ein Knoten nur einen Wert enthält,
setzen wir \lstinline{content_right = -1},
und der Zeiger \lstinline{middle} wird nicht verwendet.
\item
Wenn wir neue Werte in den Baum einfügen,
werden \emph{zuerst\/} die nicht voll besetzten Knoten aufgefüllt
und \emph{danach erst\/} neue Knoten angelegt und Zeiger gesetzt.
\item
Beim Auffüllen eines Knotens darf nötigenfalls \lstinline{content_left}
nach \lstinline{content_right} verschoben werden.
Ansonsten werden einmal angelegte Knoten nicht mehr verändert.
\end{itemize}
\vspace*{-\medskipamount}
(In der Praxis dürfen Knoten gemäß speziellen Regeln
nachträglich verändert werden,
um Entartungen gar nicht erst entstehen zu lassen --
siehe z.\,B.\ \url{https://de.wikipedia.org/wiki/2-3-4-Baum}.)
\begin{enumerate}[\quad(a)]
\item
Zeichnen Sie ein Schaubild, das veranschaulicht,
wie die Zahlen 7, 137, 3, 5, 6, 42, 1, 2 und 12
nacheinander und in dieser Reihenfolge
in den oben beschriebenen Baum eingefügt werden
-- analog zu den Vortragsfolien (\gitfile{20170123}{hp-20170123.pdf}),
Seite 21.
%
% Lösung:
%
% 7 137
% / |
% 3 5 12 42
% / \
% 1 2 6
%
% (NULL-Zeiger sind hier nicht dargestellt,
% gehören aber dazu.)
%
\item
Dasselbe, aber in der Reihenfolge
2, 7, 42, 12, 1, 137, 5, 6, 3.
%
% Lösung:
%
% 2 7
% / | \
% 1 5 6 12 42
% / \
% 3 137
%
% (NULL-Zeiger sind hier wieder nicht dargestellt,
% gehören aber dazu.)
%
\item
Beschreiben Sie in Worten und/oder als C-Quelltext-Fragment,
wie eine Funktion aussehen müßte, um den auf diese Weise entstandenen Baum
sortiert auszugeben.
\end{enumerate}
\solution
\begin{enumerate}[\quad(a)]
\item
\textbf{Zeichnen Sie ein Schaubild, das veranschaulicht,
wie die Zahlen 7, 137, 3, 5, 6, 42, 1, 2 und 12
nacheinander und in dieser Reihenfolge
in den oben beschriebenen Baum eingefügt werden
-- analog zu den Vortragsfolien (\gitfile{20170123}{hp-20170123.pdf}),
Seite 21.}
%
% Lösung:
%
% 7 137
% / |
% 3 5 12 42
% / \
% 1 2 6
%
% (NULL-Zeiger sind hier nicht dargestellt,
% gehören aber dazu.)
%
\begin{center}
\newcommand{\x}{~\makebox(0,0){\raisebox{0.7em}{\rule{1pt}{1.4em}}}~}
\begin{tikzpicture}
\color{blendedblue}
\node(root) at (0,0) {\lstinline{node *root;}};
\node[shape=rectangle,draw,line width=1pt](7-137) at (0,-1.5) {7\x 137};
\draw[-latex](root)--(7-137);
\node[shape=rectangle,draw,line width=1pt](3-5) at (-2,-3) {3\x 5};
\draw[-latex](7-137)--(3-5);
\node[shape=rectangle,draw,line width=1pt](12-42) at (0,-3) {12\x 42};
\draw[-latex](7-137)--(12-42);
\node[shape=rectangle,draw,line width=1pt](1-2) at (-4,-4.5) {1\x 2};
\draw[-latex](3-5)--(1-2);
\node[shape=rectangle,draw,line width=1pt](6) at (0,-4.5) {6};
\draw[-latex](3-5)--(6);
\end{tikzpicture}
\end{center}
Bemerkungen:
\begin{itemize}
\item
Zeiger mit dem Wert \lstinline{NULL} sind nicht dargestellt:
\lstinline{right}-Zeiger von 7/137,
\lstinline{middle}-Zeiger von 3/5,
sämtliche Zeiger von 1/2, 12/42 und 6.
\item
Beim Einfügen der 12 wird die sich bereits vorher in diesem
\lstinline{node} befindliche 42 zu \lstinline{content_right},
und die 12 wird das neue \lstinline{content_left}.
\item
Dieser Baum hat sehr einfache Regeln und ist daher \emph{nicht\/}
balanciert. Insbesondere unsere Regel, daß einmal angelegte Knoten
nicht mehr verändert werden dürfen, steht dem im Wege.
Ein einfaches Beispiel für einen \emph{balancierten\/} ternären
Baum ist der 2-3-Baum -- siehe z.\,B.\ \url{https://en.wikipedia.org/wiki/2-3_tree}.
\end{itemize}
\item
\textbf{Dasselbe, aber in der Reihenfolge
2, 7, 42, 12, 1, 137, 5, 6, 3.}
%
% Lösung:
%
% 2 7
% / | \
% 1 5 6 12 42
% / \
% 3 137
%
% (NULL-Zeiger sind hier wieder nicht dargestellt,
% gehören aber dazu.)
%
\begin{center}
\newcommand{\x}{~\makebox(0,0){\raisebox{0.7em}{\rule{1pt}{1.4em}}}~}
\begin{tikzpicture}
\color{blendedblue}
\node(root) at (0,0) {\lstinline{node *root;}};
\node[shape=rectangle,draw,line width=1pt](2-7) at (0,-1.5) {2\x 7};
\draw[-latex](root)--(7-137);
\node[shape=rectangle,draw,line width=1pt](1) at (-2,-3) {1};
\draw[-latex](2-7)--(1);
\node[shape=rectangle,draw,line width=1pt](5-6) at (0,-3) {5\x 6};
\draw[-latex](2-7)--(5-6);
\node[shape=rectangle,draw,line width=1pt](3) at (-2,-4.5) {3};
\draw[-latex](5-6)--(3);
\node[shape=rectangle,draw,line width=1pt](12-42) at (2,-3) {12\x 42};
\draw[-latex](2-7)--(12-42);
\node[shape=rectangle,draw,line width=1pt](137) at (4,-4.5) {137};
\draw[-latex](12-42)--(137);
\end{tikzpicture}
\end{center}
Bemerkungen:
\begin{itemize}
\item
Wieder sind Zeiger mit dem Wert \lstinline{NULL} nicht dargestellt:
\lstinline{middle}- und \lstinline{right}-Zeiger von 5/6,
\lstinline{left}- und \lstinline{middle}-Zeiger von 12/42,
sämtliche Zeiger von 1, 3 und 137.
\item
Beim Einfügen der 12 wird wieder die sich bereits vorher in diesem
\lstinline{node} befindliche 42 zu \lstinline{content_right},
und die 12 wird das neue \lstinline{content_left}.
\end{itemize}
\item
\textbf{Beschreiben Sie in Worten und/oder als C-Quelltext-Fragment,
wie eine Funktion aussehen müßte, um den auf diese Weise entstandenen Baum
sortiert auszugeben.}
Die entscheidende Idee ist \textbf{Rekursion}.
Eine Funktion, die den gesamten Baum ausgibt,
müßte einmalig für den Zeiger \lstinline{root} aufgerufen werden
und folgendes tun:
\begin{enumerate}[\quad 1.]
\item
falls der übergebene Zeiger den Wert \lstinline{NULL} hat,
nichts ausgeben, sondern die Funktion direkt beenden,
\item
sich selbst für den \lstinline{left}-Zeiger aufrufen,
\item
den Wert von \lstinline{content_left} ausgeben,
\item
sich selbst für den \lstinline{middle}-Zeiger aufrufen,
\item
sofern vorhanden (also ungleich \lstinline{-1}),
den Wert von \lstinline{content_right} ausgeben,
\item
sich selbst für den \lstinline{right}-Zeiger aufrufen.
\end{enumerate}
Als C-Fragment:
\begin{lstlisting}[gobble=8]
void output_tree (node *root)
{
if (root)
{
output_tree (root->left);
printf ("%d\n", root->content_left);
output_tree (root->middle);
if (root->content_right >= 0)
printf ("%d\n", root->content_right);
output_tree (root->right);
}
}
\end{lstlisting}
Die Datei \gitfile{20170123}{loesung-1c.c} erweitert dieses Fragment
zu einem vollständigen C-Programm zum Erstellen und sortierten Ausgeben
eines ternären Baums mit den Zahlenwerten von Aufgabenteil (a).
\end{enumerate}
\exercise{Aufräumen}
Im Zuge der Übungsaufgaben von letzter Woche
(\gitfile{20170116}{hp-uebung-20170116.pdf} --
\emph{Stack-Operationen, Iterativer Floodfill, Doppelt verkettete Liste\/})
wurden verschiedene Funktionen zur Manipulation von Arrays und Listen erstellt.
Überarbeiten Sie diese Funktionen derart,
daß das Programm auch bei unsinnigen Eingabewerten nicht abstürzt,
sondern eine Fehlermeldung ausgibt und sich kontrolliert beendet
oder kontrolliert weiterläuft.
Hinweis: Es ist in C leider nicht möglich,
bei einem Zeiger zwischen einem sinnvollen und einem zufälligen Wert
zu unterscheiden. Wir müssen uns daher bei Zeigern damit begnügen,
zwischen sinvollen Zeigern auf Daten und dem Wert \lstinline{NULL}
zu unterscheiden.
\solution
Wir nehmen die Überarbeitung an den jeweils fortgeschrittensten Versionen
der Programme vor: \gitfile{20170123}{loesung-2-1d.c}
ist eine Überarbeitung von \gitfile{20170116}{loesung-1d.c},
und \gitfile{20170123}{loesung-2-3b.c}
ist eine Überarbeitung von \gitfile{20170116}{loesung-3b.c}.
Wichtige Punkte:
\begin{itemize}
\item
\textbf{Vor jedem Zugriff auf ein Array muß sichergestellt werden,
daß sich der Index im gültigen Bereich befindet.}
In \gitfile{20170123}{loesung-2-1d.c} ist dies in den Funktionen
\lstinline{push()}, \lstinline{pop()}, \lstinline{insert()}
und \lstinline{insert_sorted()} notwendig.
Die Funktionen \lstinline{show()} und \lstinline{search()}
sind so geschrieben, daß dieses Problem nicht auftreten kann.
\item
In \gitfile{20170123}{loesung-2-1d.c} könnte man strenggenommen
noch prüfen, ob die Variable \lstinline{stack_pointer} möglicherweise
durch Manipulation von außen einen negativen und daher sinnlosen
Wert bekommen hat.
Alternativ könnte man aus \lstinline{stack_pointer}
eine \lstinline{unsigned}-Variable machen,
die keine negativen Werte annehmen kann sondern ggf.\ überläuft
und einen großen positven Wert annimmt,
der dann durch die Bedingung \lstinline{stack_pointer < STACK_SIZE}
abgefangen wird.
\item
\textbf{Vor jedem Zugriff auf einen Zeiger muß sichergestellt sein,
daß der Zeiger nicht den Wert \lstinline{NULL} hat.}
In \gitfile{20170123}{loesung-2-3b.c} ist dies in den Funktionen
\lstinline{insert_into_list()} und \lstinline{delete_from_list()} notwendig.
Die Funktionen \lstinline{check_list()}, \lstinline{output_list()}
prüfen dies bereits automatisch.
Man beachte, daß der Zeigerzeiger \lstinline{node **first}
in \lstinline{delete_from_list()} nicht \lstinline{NULL} sein darf,
während dies für den Zeiger \lstinline{*first}, auf den er zeigt,
kein Problem darstellt.
\item
Während des "`Aufräumens"' in \gitfile{20170123}{loesung-2-3b.c}
ist aufgefallen, daß die Initialisierung der Zeiger
\lstinline{next} und \lstinline{prev} in \lstinline{node *element5} fehlte.
Diese Art von Fehler -- Zeiger mit \emph{zufälligen\/} Werten -- läßt sich
durch Prüfung der Zeigerwerte \emph{nicht\/} abfangen.
\item
Die Hauptprogramme enthalten Testfälle für die Fehlerbehandlung.
Weil das Programm nach jedem Fehler abbricht, kann man diese nicht gleichzeitig,
sondern nur einzeln testen. Aus diesem Grunde sind alle Testfälle bis auf einen
auskommentiert.
\end{itemize}
\end{document}
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int content_left, content_right;
struct node *left, *middle, *right;
} node;
void insert_into_tree (node **root, int value)
{
if (*root)
{
if ((*root)->content_right >= 0)
if (value < (*root)->content_left)
insert_into_tree (&(*root)->left, value);
else if (value < (*root)->content_right)
insert_into_tree (&(*root)->middle, value);
else
insert_into_tree (&(*root)->right, value);
else
if (value < (*root)->content_left)
{
(*root)->content_right = (*root)->content_left;
(*root)->content_left = value;
}
else
(*root)->content_right = value;
}
else
{
*root = malloc (sizeof (node));
(*root)->left = NULL;
(*root)->content_left = value;
(*root)->middle = NULL;
(*root)->content_right = -1;
(*root)->right = NULL;
}
}
void output_tree (node *root)
{
if (root)
{
output_tree (root->left);
printf ("%d\n", root->content_left);
output_tree (root->middle);
if (root->content_right >= 0)
printf ("%d\n", root->content_right);
output_tree (root->right);
}
}
int main (void)
{
node *root = NULL;
insert_into_tree (&root, 7);
insert_into_tree (&root, 137);
insert_into_tree (&root, 3);
insert_into_tree (&root, 5);
insert_into_tree (&root, 6);
insert_into_tree (&root, 42);
insert_into_tree (&root, 1);
insert_into_tree (&root, 2);
insert_into_tree (&root, 12);
output_tree (root);
return 0;
}
#include <stdio.h>
#include <error.h>
#define STACK_SIZE 10
int stack[STACK_SIZE];
int stack_pointer = 0;
void push (int x)
{
if (stack_pointer >= STACK_SIZE)
error (1, 0, "push(): stack overflow");
stack[stack_pointer++] = x;
}
int pop (void)
{
if (stack_pointer == 0)
error (1, 0, "pop(): stack is empty");
return stack[--stack_pointer];
}
void show (void)
{
printf ("stack content:");
for (int i = 0; i < stack_pointer; i++)
printf (" %d", stack[i]);
if (stack_pointer)
printf ("\n");
else
printf (" (empty)\n");
}
void insert (int x, int pos)
{
if (pos < 0 || pos >= STACK_SIZE)
error (1, 0, "insert(): position %d out of range (0, ..., %d)",
pos, STACK_SIZE - 1);
for (int i = stack_pointer - 1; i >= pos; i--)
stack[i + 1] = stack[i];
stack[pos] = x;
stack_pointer++;
}
void insert_sorted (int x)
{
if (stack_pointer >= STACK_SIZE)
error (1, 0, "insert_sorted(): stack overflow");
int i = stack_pointer - 1;
while (i >= 0 && x < stack[i])
{
stack[i + 1] = stack[i];
i--;
}
stack[i + 1] = x;
stack_pointer++;
}
int search (int x)
{
int left = 0;
int right = stack_pointer;
while (left < right - 1)
{
int middle = (left + right) / 2;
if (x < stack[middle])
right = middle;
else
left = middle;
}
if (x == stack[left])
return left;
else
return -1;
}
int main (void)
{
push (3);
push (7);
push (137);
show ();
insert (5, 1);
show ();
insert_sorted (42);
show ();
insert_sorted (2);
show ();
printf ("%d\n", search (2));
printf ("%d\n", search (4));
printf ("%d\n", search (42));
printf ("%d\n", search (67));
printf ("%d\n", search (137));
printf ("%d\n", search (1117));
// insert (512, -1);
// insert (512, 42);
// for (int i = 255; i > 128; i--)
// push (i);
for (int i = 255; i > 128; i--)
insert_sorted (i);
// for (int i = 0; i < 1000; i++)
// printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
typedef struct node
{
int content;
struct node *next, *prev;
} node;
void check_list (node *first)
{
for (node *p = first; p; p = p->next)
{
if (p->next && p->next->prev != p)
fprintf (stderr, "List inconsistency!\n");
if (p->prev && p->prev->next != p)
fprintf (stderr, "List inconsistency!\n");
}
}
void output_list (node *first)
{
for (node *p = first; p; p = p->next)
printf ("%d ", p->content);
printf ("\n");
}
void insert_into_list (node *what, node *where)
{
if (what == NULL)
error (1, 0, "insert_into_list(): first parameter is NULL");
if (where == NULL)
error (1, 0, "insert_into_list(): second parameter is NULL");
what->next = where->next;
if (where->next)
where->next->prev = what;
what->prev = where;
where->next = what;
}
void delete_from_list (node *what, node **first)
{
if (what == NULL)
error (1, 0, "delete_from_list(): first parameter is NULL");
if (first == NULL)
error (1, 0, "delete_from_list(): second parameter is NULL");
if (what == *first)
{
*first = what->next;
if (*first)
(*first)->prev = NULL;
}
else
{
node *p = *first;
while (p && p->next != what)
p = p->next;
if (p)
p->next = what->next;
if (what->next)
what->next->prev = p;
}
free (what);
}
int main (void)
{
node *element3 = malloc (sizeof (node));
node *element7 = malloc (sizeof (node));
node *element137 = malloc (sizeof (node));
element3->content = 3;
element7->content = 7;
element137->content = 137;
node *first = element3;
element3->prev = NULL;
element3->next = element7;
element7->prev = element3;
element7->next = element137;
element137->prev = element7;
element137->next = NULL;
output_list (first);
check_list (first);
node *element5 = malloc (sizeof (node));
element5->content = 5;
element5->next = NULL;
element5->prev = NULL;
insert_into_list (element5, element3);
output_list (first);
check_list (first);
delete_from_list (element5, &first);
output_list (first);
check_list (first);
delete_from_list (element3, &first);
output_list (first);
check_list (first);
delete_from_list (element137, &first);
output_list (first);
check_list (first);
// insert_into_list (NULL, element7);
node *element42 = malloc (sizeof (node));
element42->content = 42;
element42->next = NULL;
element42->prev = NULL;
insert_into_list (element42, NULL);
// delete_from_list (NULL, &first);
// delete_from_list (element7, NULL);
return 0;
}
......@@ -48,6 +48,8 @@ Tafelbilder:
* [02.01.2017: SQL Injection](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170102/photo-20170102-130102.jpg)
* [09.01.2017: Virtuelle Methodentabellen](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170109/photo-20170109-124857.jpg)
* [23.01.2017: Binärer Baum, Klausur-WLAN, Praktikumstermin](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170123/photo-20170123-125804.jpg)
* [30.01.2017: Musterlösung zu den Übungen vom 23.1.2017](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170130/photo-20170130-130345.jpg)
* [30.01.2017: Erläuterungen zu Übungsaufgabe 4 vom 19.12.2016](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170130/photo-20170130-150616.jpg)
Übungsaufgaben:
---------------
......@@ -82,6 +84,7 @@ Musterlösungen zu den Übungsaufgaben:
* [19.12.2016: Bürgerentscheid, Lokale Variable im Speicher, Blinkende LEDs, objektorientierte Tier-Datenbank](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20161219/hp-musterloesung-20161219.pdf)
* [09.01.2017: Objektorientierte Programmierung mit dem C-Datentyp _union, objektorientierte Tier-Datenbank](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170109/hp-musterloesung-20170109.pdf)
* [16.01.2017: Stack-Operationen, Iterativer Floodfill, Doppelt verkettete Listen](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170116/hp-musterloesung-20170116.pdf)
* [23.01.2017: Ternäre Bäume, Aufräumen](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20170123/hp-musterloesung-20170123.pdf)
Praktikumsunterlagen:
---------------------
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment