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

Vorbereitung 30.11.2023

parent 69b943a5
No related branches found
No related tags found
No related merge requests found
Showing
with 1377 additions and 2 deletions
No preview for this file type
......@@ -551,6 +551,7 @@
\begin{picture}(0,0)
\put(8,-6.5){\includegraphics{pendulum.pdf}}
\put(5,-1.5){\rotatebox{10}{\color{red}\bf\textarrow\ nächste Woche}}
\end{picture}
\begin{eqnarray*}
......
No preview for this file type
% hp-musterloesung-20231116.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% hp-musterloesung-20231123.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Peter Gerwinski
%
% This document is free software: you can redistribute it and/or
......@@ -30,7 +30,7 @@
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 16.\ November 2023}
Musterlösung zu den Übungsaufgaben -- 23.\ November 2023}
\exercise{Kondensator}
......
../common/Tower_of_Hanoi.jpeg
\ No newline at end of file
#include <stdio.h>
/* ... */
int main (void)
{
pbm_open (14, 14, "test.pbm");
pbm_line (" ");
pbm_line (" XXXXXX ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X XX XX X ");
pbm_line (" X X X X ");
pbm_line (" X X ");
pbm_line (" X X X X ");
pbm_line (" X X X X ");
pbm_line (" X XXXX X ");
pbm_line (" X X ");
pbm_line (" XXXXXX ");
pbm_line (" ");
pbm_close ();
return 0;
}
File added
20231130/aufgabe-1.png

162 B

#include <stdio.h>
int fak (int n)
{
if (n <= 0)
return 1;
else
return n * fak (n - 1);
}
int main (void)
{
for (int n = 0; n <= 5; n++)
printf ("%d\n", fak (n));
return 0;
}
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = pos; i < len; i++)
target[i + 1] = target[i];
target[pos] = src;
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
File added
% hp-20231130.pdf - Lecture Slides on Low-Level Programming
% Copyright (C) 2012, 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 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/>.
% README: Algorithmen: Rekursion, Aufwandsabschätzungen
\documentclass[10pt,t]{beamer}
\usepackage{pgslides}
\usepackage{tikz}
\newcommand{\redurl}[1]{\href{#1}{\color{red}\nolinkurl{#1}}}
\title{Hardwarenahe Programmierung}
\author{Prof.\ Dr.\ rer.\ nat.\ Peter Gerwinski}
\date{30.\ November 2023}
\begin{document}
\maketitleframe
\title{Hardwarenahe Programmierung}
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/hp}}}
\item[\textbf{2}] \textbf{Einführung in C}
\item[\textbf{3}] \textbf{Bibliotheken}
\item[\textbf{4}] \textbf{Hardwarenahe Programmierung}
\begin{itemize}
\vspace*{-\smallskipamount}
\item[\dots]
\color{medgreen}
\item[4.7] Binärdarstellung von Gleitkommazahlen
\item[4.8] Speicherausrichtung -- Alignment
\end{itemize}
\item[\textbf{5}] \textbf{Algorithmen}
\begin{itemize}
\color{orange}
\item[5.1] Differentialgleichungen
\color{red}
\item[5.2] Rekursion
\item[5.3] Aufwandsabschätzungen
\end{itemize}
\item[\textbf{6}] \textbf{Objektorientierte Programmierung}
\item[\textbf{7}] \textbf{Datenstrukturen}
\end{itemize}
\end{frame}
\setcounter{section}{3}
\section{Hardwarenahe Programmierung}
\setcounter{subsection}{6}
\subsection{Binärdarstellung von Gleitkommazahlen}
\begin{frame}[fragile]
\showsubsection
% (Diese Seite wurde unbewußt leer gelassen.)
Beispiel für Gleitkommazahl: $2{,}351\cdot10^5$ (oder: $2.351\times10^5$)
\smallskip
Bezeichnungen: $\text{Mantisse} \cdot 10^{\text{Exponent}}$
\smallskip
C-Schreibweise: \lstinline{2.351e5} (oder: \lstinline{2.351E5})
% \pause
\bigskip
Wie speichert man Gleitkommazahlen?
\smallskip
$m$-Bit-Zahl, davon
\begin{itemize}
\item
$e$ Bits für den Exponenten (einschließlich Vorzeichen),
\item
$1$ Bit für das Vorzeichen der Mantisse,
\item
$m - e - 1$ Bits für die Mantisse.
\end{itemize}
% \pause
\begin{picture}(0,0)
\color{red}
\put(1.95,0.65){\makebox(0,0){\tikz{\draw(0,0)--(0.5,0.25);}}}
\put(1.95,0.65){\makebox(0,0){\tikz{\draw(0,0.25)--(0.5,0);}}}
\end{picture}%
{\color{red}Trick: Mantisse als \newterm{normalisierte Zahl\/} abspeichern}
% \pause
\bigskip
Vorteil gegenüber ganzen Zahlen:\\
größerer Wertebereich bei vergleichbarem Speicherplatzbedarf
\medskip
Nachteil gegenüber ganzen Zahlen: Rundungsfehler\\
\textcolor{red}{\textarrow\
\textbf{ungeeignet} für Anwendungen, bei denen es auf jedes Bit ankommt\\
\phantom{\textarrow\ }(z.\,B.\ Verschlüsselung)}
\vspace*{-1cm}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Problem beim Arbeiten mit Gleitkommazahlen: Auslöschung von Ziffern
\begin{itemize}
\item
Zahlen aufsummieren:\\
vorher sortieren, mit der kleinsten Zahl beginnen
% \pause
\item
Ableitungen bilden:\\
Beim Bilden von Differenzquotienten\\
verliert man notwendigerweise an Präzision!\\
\textarrow\ Die Differenzen sehr sorgfältig auswählen.\\
\textarrow\ Am besten gar nicht ableiten, sondern integrieren.\\
% \pause
(Trick: Ableiten über Fourier-Transformationen)
\end{itemize}
\end{frame}
\subsection{Speicherausrichtung -- Alignment}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
#include <stdint.h>
uint8_t a;
uint16_t b;
uint8_t c;
\end{lstlisting}
% \pause
\bigskip
Speicheradresse durch 2 teilbar -- "`16-Bit-Alignment"'
\begin{itemize}
\item
2-Byte-Operation: effizienter
% \pause
\item
\dots\ oder sogar nur dann erlaubt
% \pause
\arrowitem
Compiler optimiert Speicherausrichtung
\end{itemize}
\medskip
% \pause
\begin{minipage}{3cm}
\begin{lstlisting}[gobble=6]
¡uint8_t a;
uint8_t dummy;
uint16_t b;
uint8_t c;¿
\end{lstlisting}
\end{minipage}
% \pause
\begin{minipage}{3cm}
\begin{lstlisting}[gobble=6]
¡uint8_t a;
uint8_t c;
uint16_t b;¿
\end{lstlisting}
\end{minipage}
% \pause
\vspace{-1.75cm}
\strut\hfill
\begin{minipage}{6.5cm}
Fazit:
\begin{itemize}
\item
\textbf{Adressen von Variablen\\
sind systemabhängig}
\item
Bei Definition von Datenformaten\\
Alignment beachten \textarrow\ effizienter
\end{itemize}
\end{minipage}
\end{frame}
\section{Algorithmen}
\subsection{Differentialgleichungen}
\begin{frame}[fragile]
\showsection
\showsubsection
\textbf{Beispiel 1: Gleichmäßig beschleunigte Bewegung}
\strut\hfill
\begin{minipage}{2.5cm}
\vspace*{0.6cm}
\begin{align*}
x'(t) &= v_x(t) \\[0.65cm]
y'(t) &= v_y(t) \\[0.75cm]
v_x'(t) &= 0 \\[0.65cm]
v_y'(t) &= -g
\end{align*}
\vspace*{0.0cm}
\end{minipage}%
\only<1>{\hspace*{9.49cm}}\strut
\only<2->{\hfill$\Rightarrow$\hfill}%
\begin{onlyenv}<2-8>
\begin{minipage}{8.3cm}
\begin{align*}
x(t) &= \int v_x(t)\,dt
\visible<4->{= \int v_{0x}\,dt}
\visible<5->{= x_0 + v_{0x}\cdot t}\\[\medskipamount]
y(t) &= \int v_y(t)\,dt
\visible<7->{= \int v_{0y} - g\cdot t\,dt}
\visible<8->{= y_0 + v_{0y}\cdot t
- {\textstyle\frac12}gt^2}\\[\bigskipamount]
v_x(t) &= \int 0\,dt
\visible<3->{= v_{0x}} \\[\medskipamount]
v_y(t) &= \int -g\,dt
\visible<6->{= v_{0y} - g\cdot t}
\end{align*}
\end{minipage}%
\end{onlyenv}%
\begin{onlyenv}<9->
\begin{minipage}{3.5cm}
\vspace*{0.5cm}
\begin{lstlisting}[gobble=8,xleftmargin=0.5em]
¡x += vx * dt;¿
\end{lstlisting}
\vspace{0.75cm}
\begin{lstlisting}[gobble=8,xleftmargin=0.5em]
¡y += vy * dt;¿
\end{lstlisting}
\vspace{0.90cm}
\begin{lstlisting}[gobble=8,xleftmargin=0.5em]
¡vx += 0 * dt;¿
\end{lstlisting}
\vspace{0.75cm}
\begin{lstlisting}[gobble=8,xleftmargin=0.5em]
¡vy += -g * dt;¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}{5.13cm}
% Siehe: \file{gtk-13.c}
\strut
\end{minipage}
\end{onlyenv}%
\hfill\strut
\end{frame}
\begin{frame}[fragile]
\showsection
\showsubsection
\textbf{Beispiel 1: Gleichmäßig beschleunigte Bewegung}
\medskip
\textbf{Beispiel 2: Mathematisches Pendel}
\vspace*{-2\bigskipamount}
\begin{picture}(0,0)
\put(8,-6.5){\includegraphics{pendulum.pdf}}
\end{picture}
\begin{eqnarray*}
\varphi'(t) &=& \omega(t) \\[\smallskipamount]
\omega'(t) &=& -\frac{g}{l}\cdot\sin\varphi(t)\hspace*{7.1cm}
\end{eqnarray*}
\vspace*{-1.5\medskipamount}
\begin{itemize}
\item
Von Hand (analytisch):\\
Lösung raten (Ansatz), Parameter berechnen
\item
Mit Computer (numerisch):\\
Eulersches Polygonzugverfahren
\end{itemize}
\smallskip
\begin{lstlisting}[gobble=0]
phi += dt * omega;
omega += - dt * g / l * sin (phi);
\end{lstlisting}
\pause
\bigskip
\textbf{Beispiel 3: Weltraum-Simulation}
Praktikumsaufgabe
\vspace*{-1cm}
\end{frame}
\subsection{Rekursion}
\begin{frame}[fragile]
\showsubsection
Vollständige Induktion:
\vspace*{-0.725cm}
\begin{displaymath}
\hspace*{4cm}
\left.
\begin{array}{r}
\mbox{Aussage gilt für $n = 1$}\\[2pt]
\mbox{Schluß von $n - 1$ auf $n$}
\end{array}
\right\}
\mbox{Aussage gilt für alle $n\in\mathbb{N}$}
\end{displaymath}
\vspace*{-0.5cm}
\pause
Türme von Hanoi
\begin{onlyenv}<2>
\begin{center}
\includegraphics[width=12.2cm]{Tower_of_Hanoi.jpeg}
\end{center}
\end{onlyenv}
\begin{onlyenv}<3->
\begin{itemize}
\item
64 Scheiben, 3 Plätze,
\only<3-4>{\hfill\makebox(0,0)[rt]{\includegraphics[width=6cm]{Tower_of_Hanoi.jpeg}}}\\
immer 1 Scheibe verschieben
\item
Ziel: Turm verschieben
\item
Es dürfen nur kleinere Scheiben\\
auf größeren liegen.
\bigskip
\pause
\pause
\item
$n = 1$ Scheibe: fertig
\item
Wenn $n - 1$ Scheiben verschiebbar:\\
schiebe $n - 1$ Scheiben auf Hilfsplatz,\\
verschiebe die darunterliegende,\\
hole $n - 1$ Scheiben von Hilfsplatz
\end{itemize}
\begin{onlyenv}<5>
\vspace{-4.3cm}
\begin{lstlisting}[gobble=8,xleftmargin=6.4cm]
void move (int from, int to, int disks)
{
if (disks == 1)
move_one_disk (from, to);
else
{
int help = 0 + 1 + 2 - from - to;
move (from, help, disks - 1);
move (from, to, 1);
move (help, to, disks - 1);
}
}
\end{lstlisting}
\end{onlyenv}
% \begin{onlyenv}<6->
% \vspace{-5.0cm}
% \hspace*{7.4cm}\begin{minipage}[t]{5cm}
% 32 Scheiben:
% \begin{lstlisting}[gobble=10,style=terminal]
% $ ¡time ./hanoi-9b¿
% ...
% real 0m30,672s
% user 0m30,662s
% sys 0m0,008s
% \end{lstlisting}
% \pause[7]
% \begin{itemize}
% \arrowitem
% etwas über 1 Minute\\
% für 64 Scheiben
% \end{itemize}
% \pause
% \vspace*{-0.5cm}
% \begin{picture}(0,0)
% \color{red}
% \put(0,0){\makebox(0,0)[bl]{\tikz[line width=1pt]{\draw(0,0)--(4,0.8);}}}
% \put(0,0.8){\makebox(0,0)[tl]{\tikz[line width=1pt]{\draw(0,0)--(4,-0.8);}}}
% \end{picture}
%
% Für jede zusätzliche Scheibe\\verdoppelt sich die Rechenzeit!
% % 30.672 * 2^32 / 3600 / 24 / 365.25 = 4174.43775518138261464750
% \begin{itemize}
% \arrowitem
% $\frac{30,672\,\text{s}\,\cdot\,2^{32}}{3600\,\cdot\,24\,\cdot\,365,25} \approx 4174$
% Jahre\\[\smallskipamount]
% für 64 Scheiben
% \end{itemize}
% \end{minipage}
% \end{onlyenv}
\end{onlyenv}
\end{frame}
\subsection{Aufwandsabschätzungen \protect\color{gray}-- Komplexitätsanalyse}
\begin{frame}[fragile]
% \newcommand{\w}{\hspace*{0.75pt}}
\showsubsection
\begin{picture}(0,0)
\put(7.6,-0.5){%
\begin{minipage}[t]{5.3cm}
% \vspace*{-1.0cm}\includegraphics{landau-symbols.pdf}
\vspace*{-1.0cm}\alt<16->{\includegraphics{landau-symbols-3.pdf}}%
{\alt<15->{\includegraphics{landau-symbols-2.pdf}}%
{\includegraphics{landau-symbols.pdf}}}
\small
\begin{description}\itemsep0pt\leftskip-0.5cm
\item[$n$:] Eingabedaten
\item[$g(n)$:] Rechenzeit
\end{description}
\end{minipage}}
\end{picture}
\vspace*{-\bigskipamount}
Wann ist ein Programm "`schnell"'?
\medskip
\begin{onlyenv}<1-2>
Türme von Hanoi: $\mathcal{O}(2^n)$
\par\medskip
Für jede zusätzliche Scheibe\\verdoppelt sich die Rechenzeit!
\begin{itemize}
\arrowitem
$\frac{30,672\,\text{s}\,\cdot\,2^{32}}{3600\,\cdot\,24\,\cdot\,365,25} \approx 4174$
Jahre\\[\smallskipamount]
für 64 Scheiben
\end{itemize}
\bigskip
\end{onlyenv}
\begin{onlyenv}<2->
Faustregel:\\Schachtelung der Schleifen zählen\\
$k$ Schleifen ineinander \textarrow\ $\mathcal{O}(n^k)$
\bigskip
\end{onlyenv}
\begin{onlyenv}<3-13>
\textbf{Beispiel: Sortieralgorithmen}
\smallskip
Anzahl der Vergleiche bei $n$ Strings
\begin{itemize}
\item
Maximum suchen \pause[4]mit Schummeln\pause: $\mathcal{O}(1)$
\pause
\item
Maximum suchen\pause: $\mathcal{O}(n)$
\pause
\item
Selection-Sort\pause: $\mathcal{O}(n^2)$
\pause
\item
Bubble-Sort\pause: $\mathcal{O}(n)$ bis $\mathcal{O}(n^2)$
\pause
\item
Quicksort\pause: $\mathcal{O}(n\log n)$ bis $\mathcal{O}(n^2)$
\end{itemize}
\end{onlyenv}
\begin{onlyenv}<14>
\textbf{Wie schnell ist RSA-Verschlüsselung?}
\smallskip
\begin{math}
c = m^e\,\%\,N
\end{math}
\quad
("`$\%$"' = "`modulo"')
\medskip
\begin{lstlisting}[gobble=6,xleftmargin=2em]
int c = 1;
for (int i = 0; i < e; i++)
c = (c * m) % N;
\end{lstlisting}
\smallskip
\begin{itemize}
\item
$\mathcal{O}(e)$ Iterationen
% \item
% wenn $n$ die Anzahl der Binärziffern (Bits) von $e$ ist:
% $\mathcal{O}(2^n)$ Iterationen
\item
mit Trick:
$\mathcal{O}(\log e)$ Iterationen ($\log e$ = Anzahl der Ziffern von $e$)
\end{itemize}
\smallskip
Jede Iteration enthält eine Multiplikation und eine Division.\\
Aufwand dafür: $\mathcal{O}(\log e)$\\
\textarrow\ Gesamtaufwand: $\mathcal{O}\bigl((\log e)^2\bigr)$
\end{onlyenv}
\begin{onlyenv}<15->
\textbf{Wie schnell ist RSA?}\\
\smallskip
($n$ = typische beteiligte Zahl, z.\,B. $e,p,q$)
\begin{itemize}
\item
Ver- und Entschlüsselung (Exponentiation):\\
\strut\hbox to 3.5cm{\color{red}$\mathcal{O}\!\left((\log n)^2\right)$\hss}
\only<16->{{\color{magenta}$\mathcal{O}(n^2)$}}
\item
Schlüsselerzeugung (Berechnung von $d$):\\
\strut\hbox to 3.5cm{\color{red}$\mathcal{O}\!\left((\log n)^2\right)$\hss}
\only<16->{{\color{magenta}$\mathcal{O}(n^2)$}}
\item
Verschlüsselung brechen (Primfaktorzerlegung):\\
\strut\hbox to 3.5cm{\color{red}$\mathcal{O}\bigl(2^{\sqrt{\log n\,\cdot\,\log\log n}}\bigr)$\hss}
\only<16->{{\color{magenta}$\mathcal{O}\bigl(2^{\sqrt{n\log n}}\bigr)$}}
\end{itemize}
\vspace{0cm plus 1filll}
\textbf{Die Sicherheit von RSA beruht darauf,
daß das Brechen der Verschlüsselung aufwendiger ist als
\boldmath$\mathcal{O}\bigl((\log n)^k\bigr)$ (für beliebiges $k$).}
\vspace*{0.65cm}
\end{onlyenv}
\end{frame}
\end{document}
File added
% hp-musterloesung-20231130.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 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/>.
% README: PBM-Grafik, Einfügen in Strings (Ergänzung), Fakultät
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{gnuplot-lua-tikz}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 30.\ November 2023}
\exercise{PBM-Grafik}
Bei einer PBM-Grafikdatei handelt es sich
um ein abgespeichertes C-Array von Bytes (\lstinline{uint8_t}),
das die Bildinformationen enthält:
\begin{itemize}\itemsep0pt
\item Die Datei beginnt mit der Kennung \lstinline{P4},
danach folgen Breite und Höhe in Pixel als ASCII-Zahlen,
danach ein Trennzeichen und die eigentlichen Bilddaten.
\item Jedes Bit entspricht einem Pixel.
\item Nullen stehen für Weiß, Einsen für Schwarz.
\item MSB first.
\item Jede Zeile des Bildes wird auf ganze Bytes aufgefüllt.
\end{itemize}
Viele Grafikprogramme können PBM-Dateien öffnen und bearbeiten.
Der Anfang der Datei (Kennung, Breite und Höhe)
ist auch in einem Texteditor lesbar.
Beispiel (\gitfile{hp}{2023ws/20231130}{aufgabe-1.pbm}):\hfill
\makebox(0,0)[tr]{\framebox{\includegraphics[scale=3]{aufgabe-1.png}}}
\begin{lstlisting}
P4
14 14
<Bilddaten>
\end{lstlisting}
In dem untenstehenden Programmfragment (\gitfile{hp}{2023ws/20231130}{aufgabe-1.c})
wird eine Grafik aus Textzeilen zusammengesetzt,
so daß man mit einem Texteditor "`malen"' kann:
\begin{lstlisting}
#include <stdio.h>
/* ... */
int main (void)
{
pbm_open (14, 14, "test.pbm");
pbm_line (" ");
pbm_line (" XXXXXX ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X XX XX X ");
pbm_line (" X X X X ");
pbm_line (" X X ");
pbm_line (" X X X X ");
pbm_line (" X X X X ");
pbm_line (" X XXXX X ");
pbm_line (" X X ");
pbm_line (" XXXXXX ");
pbm_line (" ");
pbm_close ();
return 0;
}
\end{lstlisting}
Ergänzen Sie das Programmfragment so,
daß es eine Datei \file{test.pbm} erzeugt, die die Grafik enthält.
Das Programm soll typische Benutzerfehler abfangen
(z.\,B.\ weniger Zeilen als in \lstinline{pbm_open} angegeben),
keine fehlerhaften Ausgaben produzieren oder abstürzen,
sondern stattdessen sinnvolle Fehlermeldungen ausgeben.
Zum Vergleich liegt eine Datei \gitfile{hp}{2023ws/20231130}{aufgabe-1.pbm}
mit dem gewünschten Ergebnis bei,\\
und die Datei \gitfile{hp}{2023ws/20231130}{aufgabe-1.png} enthält dasselbe Bild.
\points{10}
\solution
Die Datei \gitfile{hp}{2023ws/20231130}{loesung-1.c} enthält eine richtige Lösung.
Man beachte die Aufrufe der Funktion \lstinline{error()} im Falle von
falscher Benutzung der Bibliotheksfunktionen.
Weitere Erklärungen finden Sie als Kommentare im Quelltext.
Die Datei \gitfile{hp}{2023ws/20231130}{loesung-1f.c} enthält eine falsche Lösung.
(Beide Dateien unterscheiden sich nur in Zeile 46.)
Dieses Programm speichert die Bits in den Bytes von rechts nach links (LSB first).
Richtig wäre von links nach rechts (MBS first).
Das erzeugte Bild ist dementsprechend fehlerhaft.
\exercise{Fakultät}
Die Fakultät $n!$ einer ganzen Zahl $n \ge 0$ ist definiert als:
\begin{eqnarray*}
1 & \mbox{für} & n = 0, \\
n \cdot (n-1)! & \mbox{für} & n > 0.
\end{eqnarray*}
Mit anderen Worten: $n! = 1\cdot2\cdot3\cdot\dots\cdot n$.
Die folgende Funktion \lstinline{fak()} berechnet die Fakultät \emph{rekursiv}
(Datei: \gitfile{hp}{2023ws/20231130}{aufgabe-2.c}):
\begin{lstlisting}
int fak (int n)
{
if (n <= 0)
return 1;
else
return n * fak (n - 1);
}
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Schreiben Sie eine Funktion, die die Fakultät \emph{iterativ} berechnet,\\
d.\,h.\ mit Hilfe einer Schleife anstelle von Rekursion.
\points{3}
\item
Wie viele Multiplikationen (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.
\points{2}
\item
Wieviel Speicherplatz (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.
\points{3}
\end{enumerate}
\solution
\begin{itemize}
\item[(a)]
\textbf{Schreiben Sie eine Funktion, die die Fakultät \emph{iterativ} berechnet,\\
d.\,h.\ mit Hilfe einer Schleife anstelle von Rekursion.}
Datei: \gitfile{hp}{2023ws/20231130}{loesung-2.c}
\begin{lstlisting}[gobble=8]
int fak (int n)
{
int f = 1;
for (int i = 2; i <= n; i++)
f *= i;
return f;
}
\end{lstlisting}
\item[(b)]
\textbf{Wie viele Multiplikationen (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.}
In beiden Fällen werden $n$ Zahlen miteinander multipliziert --
oder $n - 1$, wenn man Multiplikationen mit 1 ausspart.
In jedem Fall hängt die Anzahl der Multiplikationen
linear von $n$ ab; es sind $\mathcal{O}(n)$ Multiplikationen.
Insbesondere arbeiten also beide Versionen gleich schnell.
\item[(c)]
\textbf{Wieviel Speicherplatz (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.}
Die iterative Version der Funktion benötigt 2 Variable vom Typ \lstinline{int},
nämlich \lstinline{n} und \lstinline{f}.
Dies ist eine konstante Zahl;
der Speicherplatzverbrauch ist daher $\mathcal{O}(1)$.
Die rekursive Version der Funktion erzeugt
jedesmal, wenn sie sich selbst aufruft,
eine zusätzliche Variable \lstinline{n}.
Es sind $n + 1$ Aufrufe; die Anzahl der Variablen \lstinline{n}
hängt linear von $n$ ab; der Speicherplatzverbrauch ist also $\mathcal{O}(n)$.
\end{itemize}
\exercise{Einfügen in Strings (Ergänzung)}
Diese Aufgabe ist eine Ergänzung von Aufgabe 3 der Übung vom 31.\ Oktober
2022 um die Teilaufgaben (e), (f) und (g). Für den "`Klausur-Modus"' können
Sie die Teilaufgaben (a) bis (d) als "`bereits gelöst"' voraussetzen.
Wir betrachten das folgende Programm (\gitfile{hp}{2023ws/20231130}{aufgabe-3.c}):
% \begin{lstlisting}[style=numbered]
\begin{lstlisting}
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = pos; i < len; i++)
target[i+1] = target[i];
target[pos] = src;
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
\end{lstlisting}
Die Ausgabe des Programms lautet:
\lstinline[style=terminal]{Hochschhhhhhhhhhh}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie, wie die Ausgabe zustandekommt.
% \points{3}
% \workspace{12}
\item
Schreiben Sie die Funktion \lstinline|insert_into_string()| so um,
daß sie den Buchstaben \lstinline{src} an der Stelle \lstinline{pos}
in den String \lstinline{target} einfügt.\par
Die Ausgabe des Programms müßte dann
\lstinline[style=terminal]{Hochschule Bochum} lauten.
% \points{2}
% \workspace{13}
\item
Was kann passieren, wenn Sie die Zeile
\lstinline{char test[100] = "Hochshule Bochum";}\\
durch
\lstinline{char test[] = "Hochshule Bochum";} ersetzen?
Begründen Sie Ihre Antwort.
% \points{2}
% \workspace{10}
\item
Was kann passieren, wenn Sie die Zeile
\lstinline{char test[100] = "Hochshule Bochum";}\\
durch
\lstinline{char *test = "Hochshule Bochum";} ersetzen?
Begründen Sie Ihre Antwort.
% \points{2}
% \workspace{10}
\item
Schreiben Sie eine Funktion
\lstinline{void insert_into_string_sorted (char src, char *target)},
die voraussetzt, daß der String \lstinline{target} alphabetisch sortiert ist
und den Buchstaben \lstinline{src} an der alphabetisch richtigen Stelle
einfügt. Diese Funktion darf die bereits vorhandene Funktion
\lstinline|insert_into_string()| aufrufen.\\
\points{4}\par
Zum Testen eignen sich die folgenden Zeilen im Hauptprogramm:
\begin{lstlisting}[gobble=8]
char test[100] = "";
insert_into_string_sorted ('c', test);
insert_into_string_sorted ('a', test);
insert_into_string_sorted ('d', test);
insert_into_string_sorted ('b', test);
\end{lstlisting}
Danach sollte \lstinline{test[]} die Zeichenfolge \lstinline{"abcd"} enthalten.
% \workspace{14}
\goodbreak
\item
Wie schnell (Landau-Symbol in Abhängigkeit von der Länge $n$ des Strings)
arbeitet Ihre Funktion
\lstinline{void insert_into_string_sorted (char src, char *target)}?
Begründen Sie Ihre Antwort.
\points{1}
% \workspace{10}
\item
Beschreiben Sie -- in Worten oder als C-Quelltext --, wie man die Funktion\\
\lstinline{void insert_into_string_sorted (char src, char *target)}
so gestalten kann,\\
daß sie in $\mathcal{O}(\log n)$ arbeitet.
\points{3}
% \workspace{35}
\end{enumerate}
\solution
\textbf{Bemerkung:} Die in dieser Aufgabe und ihrer Musterlösung vorkommenden
Funktionen prüfen nicht, ob durch das Einfügen eines Zeichens der für den
String reservierte Speicherplatz überläuft. Ein derartiges Verhalten wäre
in einem "`echten"' Programm ein \textbf{Fehler}, der katastrophale Folgen
haben kann. Wenn dergleichen hier nicht berücksichtigt wird, dann nur, um
in einer Klausur nicht den zeitlichen Rahmen zu sprengen.
\begin{enumerate}[\quad(a)]
\setcounter{enumi}{4}
\item
\textbf{Schreiben Sie eine Funktion
\lstinline{void insert_into_string_sorted (char src, char *target)},
die voraussetzt, daß der String \lstinline{target} alphabetisch sortiert ist
und den Buchstaben \lstinline{src} an der\break alphabetisch richtigen Stelle
einfügt. Diese Funktion darf die bereits vorhandene Funktion\break
\lstinline|insert_into_string()| aufrufen.}
\begin{lstlisting}{gobble=8}
void insert_into_string_sorted (char src, char *target)
{
int i = 0;
while (target[i] && target[i] < src)
i++;
insert_into_string (src, target, i);
}
\end{lstlisting}
Die Datei \gitfile{hp}{2023ws/20231130}{loesung-3e.c} enthält die o.\,a.\ Funktion
sowie zusätzliche Tests.
\item
\textbf{Wie schnell (Landau-Symbol in Abhängigkeit von der Länge $n$ des Strings)
arbeitet Ihre Funktion
\lstinline{void insert_into_string_sorted (char src, char *target)}?
Begründen Sie Ihre Antwort.}
Die Funktion sucht im Array \textbf{mittels einer Schleife}
nach der korrekten Position zum Einfügen des Zeichens
und hat daher von sich aus $\mathcal{O}(n)$.
Anschließend ruft sie die Funktion \lstinline{insert_into_string()} auf,
die ebenfalls eine Schleife verwendet, um im Array Platz zu Einfügen zu schaffen,
und daher ebenfalls $\mathcal{O}(n)$ hat.
Es bleibt daher bei $\mathcal{O}(n)$.
\item
\textbf{Beschreiben Sie -- in Worten oder als C-Quelltext --, wie man die Funktion\\
\lstinline{void insert_into_string_sorted (char src, char *target)}
so gestalten kann,\\
daß sie in $\mathcal{O}(\log n)$ arbeitet.}
In einem alphabetisch sortierten Array kann man die Suche in der Mitte beginnen
und sich durch Halbieren der Intervalle an die gesuchte Position herantasten.
Wegen des fortwährenden Halbierens geschieht dies in $\mathcal{O}(\log n)$.
(Für eine derartige Antwort gäbe es in der Klausur die volle Punktzahl.)
Wenn wir allerdings anschließend für das eigentliche Einfügen die Funktion
\lstinline{insert_into_string()} verwenden, die dafür $\mathcal{O}(n)$ benötigt,
kommen wir insgesamt auf $\mathcal{O}(n)$. Ein sortiertes Einfügen in ein Array
ist daher in $\mathcal{O}(\log n)$ nicht möglich.
(Wer dies bemerkt, kann zum einen während der Klausur nachfragen,
wie denn die Aufgabenstellung genau gemeint ist, und sich zum anderen
für die besondere Sorgfalt Zusatzpunkte verdienen.)
Die Datei \gitfile{hp}{2023ws/20231130}{loesung-3g.c} enthält einen C-Quelltext,
die den o.\,a.\ Algorithmus als Funktion implementiert.
Man beachte die Behandlung des Spezialfalls,
daß das einzufügende Zeichen am Ende angehängt werden muß.
\end{enumerate}
\end{document}
File added
% hp-uebung-20231130.pdf - Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 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/>.
% README: PBM-Grafik, Einfügen in Strings (Ergänzung), Fakultät
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{gensymb}
\newcommand{\ItwoC}{I\raisebox{0.5ex}{\footnotesize 2}C}
\newcommand{\ITWOC}{I\raisebox{0.5ex}{\normalsize 2}C}
\begin{document}
\thispagestyle{empty}
\section*{Hardwarenahe Programmierung\\
Übungsaufgaben -- 30.\ November 2023}
Diese Übung enthält Punkteangaben wie in einer Klausur.
Um zu "`bestehen"', müssen Sie innerhalb von 75 Minuten
unter Verwendung ausschließlich zugelassener Hilfsmittel
13 Punkte (von insgesamt \totalpoints) erreichen.
\exercise{PBM-Grafik}
Bei einer PBM-Grafikdatei handelt es sich
um ein abgespeichertes C-Array von Bytes (\lstinline{uint8_t}),
das die Bildinformationen enthält:
\begin{itemize}\itemsep0pt
\item Die Datei beginnt mit der Kennung \lstinline{P4},
danach folgen Breite und Höhe in Pixel als ASCII-Zahlen,
danach ein Trennzeichen und die eigentlichen Bilddaten.
\item Jedes Bit entspricht einem Pixel.
\item Nullen stehen für Weiß, Einsen für Schwarz.
\item MSB first.
\item Jede Zeile des Bildes wird auf ganze Bytes aufgefüllt.
\end{itemize}
Viele Grafikprogramme können PBM-Dateien öffnen und bearbeiten.
Der Anfang der Datei (Kennung, Breite und Höhe)
ist auch in einem Texteditor lesbar.
Beispiel (\gitfile{hp}{2023ws/20231130}{aufgabe-1.pbm}):\hfill
\makebox(0,0)[tr]{\framebox{\includegraphics[scale=3]{aufgabe-1.png}}}
\begin{lstlisting}
P4
14 14
<Bilddaten>
\end{lstlisting}
In dem untenstehenden Programmfragment (\gitfile{hp}{2023ws/20231130}{aufgabe-1.c})
wird eine Grafik aus Textzeilen zusammengesetzt,
so daß man mit einem Texteditor "`malen"' kann:
\begin{lstlisting}
#include <stdio.h>
/* ... */
int main (void)
{
pbm_open (14, 14, "test.pbm");
pbm_line (" ");
pbm_line (" XXXXXX ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X XX XX X ");
pbm_line (" X X X X ");
pbm_line (" X X ");
pbm_line (" X X X X ");
pbm_line (" X X X X ");
pbm_line (" X XXXX X ");
pbm_line (" X X ");
pbm_line (" XXXXXX ");
pbm_line (" ");
pbm_close ();
return 0;
}
\end{lstlisting}
Ergänzen Sie das Programmfragment so,
daß es eine Datei \file{test.pbm} erzeugt, die die Grafik enthält.
Das Programm soll typische Benutzerfehler abfangen
(z.\,B.\ weniger Zeilen als in \lstinline{pbm_open} angegeben),
keine fehlerhaften Ausgaben produzieren oder abstürzen,
sondern stattdessen sinnvolle Fehlermeldungen ausgeben.
Zum Vergleich liegt eine Datei \gitfile{hp}{2023ws/20231130}{aufgabe-1.pbm}
mit dem gewünschten Ergebnis bei,\\
und die Datei \gitfile{hp}{2023ws/20231130}{aufgabe-1.png} enthält dasselbe Bild.
\points{10}
Hinweis für die Klausur:
Abgabe in digitaler Form ist erwünscht, aber nicht zwingend.
\exercise{Fakultät}
Die Fakultät $n!$ einer ganzen Zahl $n \ge 0$ ist definiert als:
\begin{eqnarray*}
1 & \mbox{für} & n = 0, \\
n \cdot (n-1)! & \mbox{für} & n > 0.
\end{eqnarray*}
Mit anderen Worten: $n! = 1\cdot2\cdot3\cdot\dots\cdot n$.
Die folgende Funktion \lstinline{fak()} berechnet die Fakultät \emph{rekursiv}
(Datei: \gitfile{hp}{2023ws/20231130}{aufgabe-2.c}):
\begin{lstlisting}
int fak (int n)
{
if (n <= 0)
return 1;
else
return n * fak (n - 1);
}
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Schreiben Sie eine Funktion, die die Fakultät \emph{iterativ} berechnet,\\
d.\,h.\ mit Hilfe einer Schleife anstelle von Rekursion.
\points{3}
\item
Wie viele Multiplikationen (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.
\points{2}
\item
Wieviel Speicherplatz (Landau-Symbol)
erfordern beide Versionen der Fakultätsfunktion\\
in Abhängigkeit von \lstinline$n$?
Begründen Sie Ihre Antwort.
\points{3}
\end{enumerate}
\exercise{Einfügen in Strings (Ergänzung)}
Diese Aufgabe ist eine Ergänzung von Aufgabe 3 der Übung vom 2.\ November 2023
um die Teilaufgaben (e), (f) und (g). Für den "`Klausur-Modus"' können
Sie die Teilaufgaben (a) bis (d) als "`bereits gelöst"' voraussetzen.
Wir betrachten das folgende Programm (\gitfile{hp}{2023ws/20231130}{aufgabe-1.c}):
% \begin{lstlisting}[style=numbered]
\begin{lstlisting}
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = pos; i < len; i++)
target[i+1] = target[i];
target[pos] = src;
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
\end{lstlisting}
Die Ausgabe des Programms lautet:
\lstinline[style=terminal]{Hochschhhhhhhhhhh}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie, wie die Ausgabe zustandekommt.
% \points{3}
% \workspace{12}
\item
Schreiben Sie die Funktion \lstinline|insert_into_string()| so um,
daß sie den Buchstaben \lstinline{src} an der Stelle \lstinline{pos}
in den String \lstinline{target} einfügt.\par
Die Ausgabe des Programms müßte dann
\lstinline[style=terminal]{Hochschule Bochum} lauten.
% \points{2}
% \workspace{13}
\item
Was kann passieren, wenn Sie die Zeile
\lstinline{char test[100] = "Hochshule Bochum";}\\
durch
\lstinline{char test[] = "Hochshule Bochum";} ersetzen?
Begründen Sie Ihre Antwort.
% \points{2}
% \workspace{10}
\item
Was kann passieren, wenn Sie die Zeile
\lstinline{char test[100] = "Hochshule Bochum";}\\
durch
\lstinline{char *test = "Hochshule Bochum";} ersetzen?
Begründen Sie Ihre Antwort.
% \points{2}
% \workspace{10}
\item
Schreiben Sie eine Funktion
\lstinline{void insert_into_string_sorted (char src, char *target)},
die voraussetzt, daß der String \lstinline{target} alphabetisch sortiert ist
und den Buchstaben \lstinline{src} an der alphabetisch richtigen Stelle
einfügt. Diese Funktion darf die bereits vorhandene Funktion
\lstinline|insert_into_string()| aufrufen.\\
\points{4}\par
Zum Testen eignen sich die folgenden Zeilen im Hauptprogramm:
\begin{lstlisting}[gobble=8]
char test[100] = "";
insert_into_string_sorted ('c', test);
insert_into_string_sorted ('a', test);
insert_into_string_sorted ('d', test);
insert_into_string_sorted ('b', test);
\end{lstlisting}
Danach sollte \lstinline{test[]} die Zeichenfolge \lstinline{"abcd"} enthalten.
% \workspace{14}
\goodbreak
\item
Wie schnell (Landau-Symbol in Abhängigkeit von der Länge $n$ des Strings)
arbeitet Ihre Funktion
\lstinline{void insert_into_string_sorted (char src, char *target)}?
Begründen Sie Ihre Antwort.
\points{1}
% \workspace{10}
\item
Beschreiben Sie -- in Worten oder als C-Quelltext --, wie man die Funktion\\
\lstinline{void insert_into_string_sorted (char src, char *target)}
so gestalten kann,\\
daß sie in $\mathcal{O}(\log n)$ arbeitet.
\points{3}
% \workspace{35}
\end{enumerate}
\begin{flushright}
\textit{Viel Erfolg!}
\end{flushright}
\makeatletter
\immediate\write\@mainaux{\string\gdef\string\totalpoints{\arabic{points}}}
\makeatother
\end{document}
../common/landau-symbols-2.pdf
\ No newline at end of file
../common/landau-symbols-3.pdf
\ No newline at end of file
../common/landau-symbols.pdf
\ No newline at end of file
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <error.h>
/* Die Aufgabe besteht darin, die Funktionen pbm_open(),
* pbm_line() und pbm_close() zu schreiben.
*/
int pbm_width = 0;
int pbm_height = 0;
int line_no = 0;
FILE *pbm_file = NULL; /* globale Variable für die PBM-Datei */
void pbm_open (int width, int height, char *filename)
{
pbm_file = fopen (filename, "w");
if (!pbm_file)
error (errno, errno, "pbm_open(): cannot open file %s for writing", filename);
pbm_width = width;
pbm_height = height;
fprintf (pbm_file, "P4\n%d %d\n", pbm_width, pbm_height);
}
void pbm_line (char *line)
{
if (!pbm_file)
error (1, 0, "pbm_line(): PBM file not open");
if (!line)
error (1, 0, "pbm_line(): line is NULL");
if (strlen (line) != pbm_width)
error (1, 0, "pbm_line(): line length does not match width");
if (line_no >= pbm_height)
error (1, 0, "pbm_line(): too many lines");
int pbm_bytes = (pbm_width + 7) / 8; /* benötigte Bytes pro Zeile (immer aufrunden) */
uint8_t buffer[pbm_bytes];
for (int i = 0; i < pbm_bytes; i++) /* Puffer auf 0 initialisieren */
buffer[i] = 0;
line_no++;
for (int x = 0; line[x]; x++)
{
int i = x / 8; /* In welches Byte des Puffers gehört dieses Pixel? */
int b = x % 8; /* Welches Bit innerhalb des Bytes ist dieses Pixel? */
if (line[x] != ' ') /* Kein Leerzeichen --> Bit auf 1 setzen */
buffer[i] |= 0x80 >> b; /* MSB first. LSB first wäre 1 << b. */
}
for (int i = 0; i < pbm_bytes; i++) /* Puffer in Datei ausgeben */
fprintf (pbm_file, "%c", buffer[i]);
}
void pbm_close (void)
{
if (!pbm_file)
error (1, 0, "pbm_close(): PBM file not open");
if (line_no < pbm_height)
error (1, 0, "pbm_close(): too few lines");
fclose (pbm_file);
}
int main (void)
{
pbm_open (14, 14, "test.pbm");
pbm_line (" ");
pbm_line (" XXXXXX ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X XX XX X ");
pbm_line (" X X X X ");
pbm_line (" X X ");
pbm_line (" X X X X ");
pbm_line (" X X X X ");
pbm_line (" X XXXX X ");
pbm_line (" X X ");
pbm_line (" XXXXXX ");
pbm_line (" ");
pbm_close ();
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment