diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ed88099 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/P01_Erste_Schritte_mit_C/solution/converter.c b/P01_Erste_Schritte_mit_C/solution/converter.c new file mode 100644 index 0000000..2538f6c --- /dev/null +++ b/P01_Erste_Schritte_mit_C/solution/converter.c @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include + +#define ROW_COUNT 8 + +/** + * @brief Main entry point. + * @param[in] argc The size of the argv array. + * @param[in] argv The command line arguments... + * @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) if there is an input error. + */ +int main(char argc, char* argv){ + uint16_t i; + double rate; + + printf("Umwandlungskurs eingeben (1.00 BTC -> CHF): "); + if (1!= scanf("%lf", &rate)){ + printf("Ungültige Eingabe! Geben Sie eine Fliesskommazahl ein.\n"); + return 1; + } + + for(i = 1; i <= ROW_COUNT; i++) { + printf("%5d CHF\t<-->\t %5.5lf BTC\n", i*200, (double)i*200/rate); + } + return 0; +} \ No newline at end of file diff --git a/P01_Erste_Schritte_mit_C/solution/hello.c b/P01_Erste_Schritte_mit_C/solution/hello.c new file mode 100644 index 0000000..64d2c41 --- /dev/null +++ b/P01_Erste_Schritte_mit_C/solution/hello.c @@ -0,0 +1,25 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include + +/** + * @brief Main entry point. + * @param[in] argc The size of the argv array. + * @param[in] argv The command line arguments... + * @returns Returns EXIT_SUCCESS (=0) on success. + */ +int main(char argc, char* argv[]) { + printf("hello world.\n"); + return 0; +} \ No newline at end of file diff --git a/P01_Erste_Schritte_mit_C/solution/wordcount.c b/P01_Erste_Schritte_mit_C/solution/wordcount.c new file mode 100644 index 0000000..0a9f182 --- /dev/null +++ b/P01_Erste_Schritte_mit_C/solution/wordcount.c @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include + +/** + * @brief Main entry point. + * @param[in] argc The size of the argv array. + * @param[in] argv The command line arguments... + * @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) there is an expression syntax error. + */ +int main(char argc, char* argv[]) { + uint16_t char_count = 0, word_count = 0; + char c; + uint8_t is_word = 0; + + printf("Eingabe: "); + c=getchar(); + while('\n' != c){ + char_count++; + if(c != ' ' && c !='\t'){ + if(! is_word){ + is_word = 1; + word_count++; + } + } else { + is_word = 0; + } + c = getchar(); + } + + printf("%d Zeichen, %d W\u00F6rter\n", char_count, word_count); + return 0; + + +} \ No newline at end of file diff --git a/P02_Funktionen_Datentyp_enum/README_solution.md b/P02_Funktionen_Datentyp_enum/README_solution.md new file mode 100644 index 0000000..5d5cf18 --- /dev/null +++ b/P02_Funktionen_Datentyp_enum/README_solution.md @@ -0,0 +1,125 @@ + +# Lösungsskizzen +## Aufgabe 1 +``` +/** + * Tage Pro Monat + * + * Das Programm liest einen Monat (1-12) und ein Jahr (1600-2400) ein und + * gibt die Anzahl der Tage dieses Monats aus. + * + * @author Gerrit Burkert, Adaptation bazz + * @version 15-FEB-2013, 16-OCT-2017, 17-OCT-2019, 16-FEB-2022 + */ + +#include +#include + +#define ERROR_IN_MONTH 1 +#define ERROR_IN_YEAR 2 + +///// Student Code + + + +// Konstante Werte fuer die Monate +// =============================== + +enum { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ }; + + +// Eingabe pruefen (0 ist vom atoi als Fehelcode verwendet und darf nicht verwendet werden) +// =============== + +int gibIntWert(char *name, int von, int bis) { + + int wert; + char wertS[20]; // + + do { + printf("%s: ", name); + fgets(wertS, 20, stdin); + wert = atoi(wertS); + if (wert < von || wert > bis) { + printf("Der Wert muss zwischen %d und %d sein.\n", von, bis); + } else { + break; + } + } while(1); + return wert; +} + + +// Schaltjahr bestimmen +// ==================== + +int istSchaltjahr(int jahr){ + + if ( (jahr % 400 == 0) || ( (jahr %100 != 0) && (jahr % 4 ==0) ) ) + return 1; + else + return 0; +} + + +// Berechnung Anzahl Tage pro Monat +// ================================ + +int tageProMonat(int jahr, int monat) { + + int anzTage; + + // Tage pro Monat bestimmen + switch (monat) { + + // Monate mit 31 Tagen + case JAN: case MAR: case MAI: case JUL: case AUG: case OKT: case DEZ: + anzTage = 31; + break; + + // Monate mit 30 Tagen + case APR: case JUN: case SEP: case NOV: + anzTage = 30; + break; + + // Februar: 28 oder 29 Tage + case FEB: + + if (istSchaltjahr(jahr)) { + anzTage = 29; + } else { + anzTage = 28; + } + break; + } + + return anzTage; +} + +///// END Student Code + + +int main (int argc, char *argv[]) { + + int monat, jahr; + + // Monat einlesen und Bereich ueberpruefen + monat = gibIntWert("Monat", 1, 12); + jahr = gibIntWert("Jahr", 1600, 9999); + + // Ausgabe zum Test + printf("Monat: %d, Jahr: %d \n", monat, jahr); + + // Ausgabe zum Test (hier mit dem ternaeren Operator "?:") + printf("%d ist %s Schaltjahr\n", jahr, istSchaltjahr(jahr) ? "ein" : "kein"); + + // Ausgabe + printf("Der Monat %02d-%d hat %d Tage.\n", monat, jahr, tageProMonat(jahr, monat)); + + return 0; +} + +``` +## Aufgabe 2 +Alter bestehender Boilerplate Code + diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java new file mode 100644 index 0000000..35570de --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java @@ -0,0 +1,88 @@ +/***************************************************************************** + M. Thaler, Jan. 2000 + Datei read.java + Funktion: Unsigned int Zahl via Bytestream von stdin einlesen. + Es wird zuerst eine Zeile eingelesen und dann konvertiert. + Die eingelesene Zeile darf nur eine Zahl enthalten + (mit optionalen Leerzeichen vor/nach der Zahl). + Returns: Die konvertierte Zahl + oder -1 (PARSE_ERROR) wenn keine Zahl oder zu gross + oder -2 (READ_ERROR) wenn Fehler beim einlesen. + Korrekturen: - Maerz 2002: M. Thaler, H. Fierz, Mar. 2002 + liest bis EOL oder EOF, korrekter Rueckgabewert + - Sept 2016: A. Gieriet + Refactored (sprechende Variablen-Namen, + MS Windows Support, 0 Wert erlaubt, + Leerzeichen Support, + "magic" Numbers durch Symbole ersetzt, + maxResult Parameter, etc.) +******************************************************************************/ + +public class read { + public int getInt(int maxResult) + throws java.io.IOException + { + // end of input + int EOF = -1; // end of file + int EOL = 10; // end of line + // abnormal return values + int PARSE_ERROR = -1; + int READ_ERROR = -2; + // ASCII Codes + int ASCII_SPACE = 32; // ' ' + int ASCII_DIGIT_0 = 48; // '0' + int ASCII_DIGIT_9 = 57; // '9' + + // conversion buffer + int NO_POS = -1; + int BUFFERSIZE = 10; + byte[] buffer = new byte[BUFFERSIZE]; + + int result = 0; + + // read line: up to EOL or EOF (i.e. error while reading) + int bytes = 0; + int input = System.in.read(); + while ((input != EOL) && (input != EOF)) { // read whole line + if (bytes < BUFFERSIZE) { // only buffer first n characters + buffer[bytes] = (byte)input; + bytes++; + } else { + result = PARSE_ERROR; // exceed buffer size, continue read line + } + input = System.in.read(); + } + if (input == EOF) { + result = READ_ERROR; + } + // check for numbers: skip leading and trailing spaces + // (i.e. this includes all control chars below the space ASCII code) + int pos = 0; + while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP + int posOfFirstDigit = pos; + int posOfLastDigit = NO_POS; + while ((pos < bytes) + && (buffer[pos] >= ASCII_DIGIT_0) + && (buffer[pos] <= ASCII_DIGIT_9)) + { + posOfLastDigit = pos; + pos++; + } + while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP + // produce return value + if (result != 0) { + // previously detected read or parse error given + } else if ((pos != bytes) || (posOfLastDigit == NO_POS)) { + result = PARSE_ERROR; + } else { // convert number + for(int i = posOfFirstDigit; i <= posOfLastDigit; i++) { + result = result * 10 + (buffer[i] - ASCII_DIGIT_0); + if (result > maxResult) { + result = PARSE_ERROR; + break; + } + } + } + return result; + } +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java new file mode 100644 index 0000000..34b1302 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java @@ -0,0 +1,32 @@ +/***************************************************************************** + M. Thaler, Jan. 2000 + Datei: rectang.java + Funktion: Bestimmt, ob Dreieck rechtwinklig ist. + Returns: true wenn rechtwinklig, sonst false. + Korrekturen: - Sept 2016, A. Gieriet + Refactored (sprechende Variablen Namen, etc.) +******************************************************************************/ + +public class rectang { + + public boolean Rectangular(int a, int b, int c) { + + int aS = a*a; + int bS = b*b; + int cS = c*c; + + boolean isRightAngled; + if ((a == 0) && (b == 0) && (c == 0)) + isRightAngled = false; + else if ((aS + bS) == cS) + isRightAngled = true; + else if ((aS + cS) == bS) + isRightAngled = true; + else if ((bS + cS) == aS) + isRightAngled = true; + else + isRightAngled = false; + + return isRightAngled; + } +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java new file mode 100644 index 0000000..8c24aca --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java @@ -0,0 +1,73 @@ +/***************************************************************************** + M. Thaler, Jan. 2000 + Datei: triangle.java + Funktion: Die drei Seiten eines Dreiecks einlesen und bestimmen ob + das Dreieck rechtwinklig ist. + Returns: Nichts. + Korrekturen: - Maerz 2002, M. Thaler, H. Fierz + Abfrage bei unkorrekter Eingabe wiederholen + - Sept 2016, A. Gieriet + Refactored (sprechende Variablen-Namen, + "magic" Numbers durch Symbole ersetzt, etc.) +******************************************************************************/ + +class triangle { + + public static void main(String[] args) + throws java.io.IOException + { + int READ_ERROR = -2; + int MAX_NUMBER = 1000; + + read ReadInt = new read(); + rectang Rect = new rectang(); + + while (true) { + System.out.println("\nDreiecksbestimmung (CTRL-C: Abbruch)\n"); + + int word = 0; + int a = 0; + int b = 0; + int c = 0; + + do { + System.out.print("Seite a: "); + word = ReadInt.getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + a = word; + else + break; + + do { + System.out.print("Seite b: "); + word = ReadInt.getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + b = word; + else + break; + + do { + System.out.print("Seite c: "); + word = ReadInt.getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + c = word; + else + break; + + if (Rect.Rectangular(a, b, c) == true) + System.out.println("-> Dreieck " + a + "-" + b + "-" + c + + " ist rechtwinklig"); + else + System.out.println("-> Dreieck " + a + "-" + b + "-" + c + + " ist nicht rechtwinklig"); + System.out.println("\n"); + } + System.out.println("\n\nbye bye\n"); + } +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c new file mode 100644 index 0000000..74c502a --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c @@ -0,0 +1,94 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include +#include "read.h" +#include "trace.h" + +//#define EOF (-1) // end of file + +/// end-of-line +#define EOL 10 // end of line + +// abnormal return values +/// parse-error +#define PARSE_ERROR (-1) + +// ASCII Codes +/// ascii code for space +#define ASCII_SPACE ' ' +/// ascii code for 0 +#define ASCII_DIGIT_0 '0' +/// ascii code for 9 +#define ASCII_DIGIT_9 '9' + +// conversion buffer +/// no buffer position sentry +#define NO_POS (-1) +/// buffer size +#define BUFFERSIZE (10) + +int getInt(int maxResult) +{ + TRACE("getInt(%d)", maxResult); + + char buffer[BUFFERSIZE] = { 0 }; + + int result = 0; + + // read line: up to EOL or EOF (i.e. error while reading) + int bytes = 0; + int input = getchar(); + while ((input != EOL) && (input != EOF)) { // read whole line + if (bytes < BUFFERSIZE) { // only buffer first n characters + buffer[bytes] = (char)input; + bytes++; + } else { + result = PARSE_ERROR; // exceed buffer size, continue read line + } + input = getchar(); + } + if (input == EOF) { + result = READ_ERROR; + } + // check for numbers: skip leading and trailing spaces + // (i.e. this includes all control chars below the space ASCII code) + int pos = 0; + while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP + int posOfFirstDigit = pos; + int posOfLastDigit = NO_POS; + while ((pos < bytes) + && (buffer[pos] >= ASCII_DIGIT_0) + && (buffer[pos] <= ASCII_DIGIT_9)) + { + posOfLastDigit = pos; + pos++; + } + while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP + // produce return value + if (result != 0) { + // previously detected read or parse error given + } else if ((pos != bytes) || (posOfLastDigit == NO_POS)) { + result = PARSE_ERROR; + } else { // convert number + for(int i = posOfFirstDigit; i <= posOfLastDigit; i++) { + result = result * 10 + (buffer[i] - ASCII_DIGIT_0); + if (result > maxResult) { + result = PARSE_ERROR; + break; + } + } + } + return result; +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h new file mode 100644 index 0000000..e3f6192 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h @@ -0,0 +1,27 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#ifndef READ_H +#define READ_H + +/// I/O error which leads to aborting. +#define READ_ERROR (-2) + +/** + * @brief Reads a line from stdin and tries to parse it as number (with optional leading and trailing spaces). + * @param[in] maxResult gives the limit for the number to be read. + * @returns the succesfully read and parsed number or a negative value if in error (READ_ERROR in case of I/O error). + */ +int getInt(int maxResult); + +#endif diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c new file mode 100644 index 0000000..4cc8f4c --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include "rectang.h" +#include "trace.h" + +int rectangular(int a, int b, int c) +{ + TRACE("rectangular(%d, %d, %d)", a, b, c); + + int aS = a*a; + int bS = b*b; + int cS = c*c; + + int isRightAngled; + if ((a == 0) && (b == 0) && (c == 0)) + isRightAngled = 0; + else if ((aS + bS) == cS) + isRightAngled = 1; + else if ((aS + cS) == bS) + isRightAngled = 1; + else if ((bS + cS) == aS) + isRightAngled = 1; + else + isRightAngled = 0; + + return isRightAngled; +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h new file mode 100644 index 0000000..38e3577 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#ifndef RECTANG_H +#define RECTANG_H + +/** + * @brief checks if the three lengths give a right-angled triangle. + * @param[in] a 1st side + * @param[in] b 2nd side + * @param[in] c 3rd side + * @returns 1 if right-angled, 0 otherwise + */ +int rectangular(int a, int b, int c); + +#endif diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h new file mode 100644 index 0000000..56f5209 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#ifndef TRACE_H +#define TRACE_H + +#include + +/** + * @brief TRACE macro. + * @param[in] MSG "..." format string literal. + * @param[in] ... optional arguments to the format string. + */ +#define TRACE(MSG, ...) do { (void)fprintf(stderr, "TRACE: " MSG "\n", ##__VA_ARGS__); } while(0) + +#endif diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c new file mode 100644 index 0000000..b1cd649 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c @@ -0,0 +1,78 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include +#include "read.h" +#include "rectang.h" +#include "trace.h" + +/// max side length +#define MAX_NUMBER 1000 + + +/** + * @brief Main entry point. + * @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) on failure. + */ +int main(void) +{ + TRACE("main()"); + + while (1) { + (void)printf("\nDreiecksbestimmung (CTRL-C: Abbruch)\n\n"); + + int word = 0; + int a = 0; + int b = 0; + int c = 0; + + do { + (void)printf("Seite a: "); + word = getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + a = word; + else + break; + + do { + (void)printf("Seite b: "); + word = getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + b = word; + else + break; + + do { + (void)printf("Seite c: "); + word = getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + c = word; + else + break; + + if (rectangular(a, b, c)) + (void)printf("-> Dreieck %d-%d-%d ist rechtwinklig\n", a, b, c); + else + (void)printf("-> Dreieck %d-%d-%d ist nicht rechtwinklig\n", a, b, c); + (void)printf("\n\n"); + } + (void)printf("\n\nbye bye\n\n"); + return EXIT_SUCCESS; +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input new file mode 100644 index 0000000..ab2d76b --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input @@ -0,0 +1,12 @@ + +a + a + a + 3 +4444 + 4444 + 44444444444444 + 4 + 4 + 5 + 5 diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input new file mode 100644 index 0000000..07b2e27 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input @@ -0,0 +1,12 @@ +3 +4 +6 +5 +4 +4 +3 +5 +5 +33 +43 +55 diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input new file mode 100644 index 0000000..fa866ac --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input @@ -0,0 +1,12 @@ +3 +4 +5 +5 +4 +3 +3 +5 +4 +33 +44 +55 diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c new file mode 100644 index 0000000..699b64a --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c @@ -0,0 +1,205 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include +#include "CUnit/Basic.h" +#include "test_utils.h" + +#ifndef TARGET // must be given by the make file --> see test target +#error missing TARGET define +#endif + +/// @brief The name of the STDOUT text file. +#define OUTFILE "stdout.txt" +/// @brief The name of the STDERR text file. +#define ERRFILE "stderr.txt" + +/// @brief The stimulus for the right-angled triangles +#define INFILE_RIGHT_ANGLED "stim-right-angled.input" +/// @brief The stimulus for the not right-angled triangles +#define INFILE_NOT_RIGHT_ANGLED "stim-not-right-angled.input" +/// @brief The stimulus for input errors +#define INFILE_ERROR "stim-error.input" + +// setup & cleanup +static int setup(void) +{ + remove_file_if_exists(OUTFILE); + remove_file_if_exists(ERRFILE); + return 0; // success +} + +static int teardown(void) +{ + // Do nothing. + // Especially: do not remove result files - they are removed in int setup(void) *before* running a test. + return 0; // success +} + + +// tests +static void test_right_angled(void) +{ + // arrange + const char *out_txt[] = { + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 3-4-5 ist rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 5-4-3 ist rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 3-5-4 ist rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 33-44-55 ist rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: \n", + "\n", + "bye bye\n", + "\n", + }; + // act + int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_RIGHT_ANGLED); + // assert + CU_ASSERT_EQUAL(exit_code, 0); + assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt)); +} + +static void test_not_right_angled(void) +{ + // arrange + const char *out_txt[] = { + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 3-4-6 ist nicht rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 5-4-4 ist nicht rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 3-5-5 ist nicht rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite b: Seite c: -> Dreieck 33-43-55 ist nicht rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: \n", + "\n", + "bye bye\n", + "\n", + }; + // act + int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_NOT_RIGHT_ANGLED); + // assert + CU_ASSERT_EQUAL(exit_code, 0); + assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt)); +} + +static void test_trace(void) +{ + // arrange + const char *err_txt[] = { + "TRACE: main()\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: rectangular(3, 4, 6)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: rectangular(5, 4, 4)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: rectangular(3, 5, 5)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: getInt(1000)\n", + "TRACE: rectangular(33, 43, 55)\n", + "TRACE: getInt(1000)\n", + }; + // act + int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_NOT_RIGHT_ANGLED); + // assert + CU_ASSERT_EQUAL(exit_code, 0); + assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt)); +} + +static void test_error(void) +{ + // arrange + const char *out_txt[] = { + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: Seite a: Seite a: Seite a: Seite a: Seite b: Seite b: Seite b: Seite b: Seite b: Seite c: Seite c: -> Dreieck 3-4-5 ist rechtwinklig\n", + "\n", + "\n", + "\n", + "Dreiecksbestimmung (CTRL-C: Abbruch)\n", + "\n", + "Seite a: \n", + "\n", + "bye bye\n", + "\n", + }; + // act + int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_ERROR); + // assert + CU_ASSERT_EQUAL(exit_code, 0); + assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt)); +} + +/** + * @brief Registers and runs the tests. + */ +int main(void) +{ + // setup, run, teardown + TestMainBasic("Triangle", setup, teardown + , test_right_angled + , test_not_right_angled + , test_trace + , test_error + ); +} diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile new file mode 100644 index 0000000..d05b3d8 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile @@ -0,0 +1,57 @@ +SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk") + +TARGET := bin/dep2dot +SOURCES := src/main.c src/data.c src/output.c +TSTSOURCES := tests/tests.c + +include $(SNP_SHARED_MAKEFILE) + + +# DEPFILES := ... define a list of png file names: %.c -> %.c.png +# BEGIN-STUDENTS-TO-ADD-CODE +DEPFILES := $(SOURCES:%.c=%.c.png) +# END-STUDENTS-TO-ADD-CODE + + +# define dep target as .PHONEY +# BEGIN-STUDENTS-TO-ADD-CODE +.PHONY: dep +# BEGIN-STUDENTS-TO-ADD-CODE + + +# define dep target depending on FULLTARGET and DEPFILES above +# action: echo some text telling that the target is done using $@ - the echo command shall not be echoed before execution +# BEGIN-STUDENTS-TO-ADD-CODE +dep: $(FULLTARGET) $(DEPFILES) + @echo "### $@ done ###" + +# BEGIN-STUDENTS-TO-ADD-CODE + + +# define new suffix rule for %.png depending on %.dot +# action: dot -Tpng $< >$@ || $(RM) $@ +# BEGIN-STUDENTS-TO-ADD-CODE +%.png: %.dot + dot -Tpng $< >$@ || $(RM) $@ + +# BEGIN-STUDENTS-TO-ADD-CODE + + +# define new suffix rule for %.dot depending on %.dep +# action: call $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@ +# BEGIN-STUDENTS-TO-ADD-CODE +%.dot: %.dep + $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@ + +# BEGIN-STUDENTS-TO-ADD-CODE + +# converts any .c file into a .c.dep file by means of GCC -H switch +# note: it removes intermediate files which were created as side effect +%.c.dep: %.c + $(COMPILE.c) -H -o $@.x $< 2>$@ && $(RM) $@.x $@.d + + +# cleanup all results, including the ones od creating the dependencies +dep-clean: clean + $(RM) $(DEPFILES) $(wildcard src/*.dep src/*.dot) + diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox new file mode 100644 index 0000000..cd6b69a --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P04 Modularisation + * + * @section Purpose + * + * This is a lab for splitting functionality into multiple modules. + * + */ diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c new file mode 100644 index 0000000..a9f28f8 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c @@ -0,0 +1,149 @@ +/** + * @file + * @brief Implementation of the dependency file access. + */ +#include "data.h" +#include "error.h" +#include +#include +#include +#include +#include + +#define MAX_PATH_LEN 512 ///< @brief Arbitrarily chosen maximum accepted path lenght. +#define MAX_LINE_LEN 512 ///< @brief Arbitrarily chosen maximum accepted line length +#define MAX_DIRS 64 ///< @brief Arbitrarily chosen maximum number of supported individual directories per dependency file. +#define MAX_FILES 256 ///< @brief Arbitrarily chosen maximum number of supported individual denendency entries. + +/** + * @brief Declaration of POSIX (but not C99) function. + * @param s [IN] The string to duplicate on the heap memory. + * @return The duplicated string. + * @remark Since the Makefile calls gcc with -std=c99, non-C99 POSIX and GNU extensions are excluded - the glibc, though, provides the function to the linker. + */ +char *strdup(const char *s); // not stdc99, but available in the glibc + +/** + * @brief Initialized the data structure before the data is to be read from th edependency file. + * @param data [INOUT] The address of the instance to initialize. + */ +static void init(data_t *data) +{ + assert(data); + memset(data, 0, sizeof(data_t)); + data->dirs = malloc(MAX_DIRS * sizeof(dir_t)); + if (!data->dirs) FATAL("no memory left"); + data->files = malloc(MAX_FILES * sizeof(file_t)); + if (!data->files) FATAL("no memory left"); +} + +/** + * @brief Updates the directory list with the given data. + * @param data [INOUT] The instance to update. + * @param path [IN] The file path of a dependency entry as given by the dependency file. + * @return The index of the directory entry (either an existing matching one or a newly added one). + * @remark Extracts the directory part by means of dirname() from the given path and looks up an existing entry or adds a new one. + */ +static size_t get_or_add_dir(data_t *data, const char *path) +{ + assert(data); + assert(path); + // The function dirname() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling dirname(). + // Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value. + char *dup = strdup(path); + if (!dup) FATAL("no memory left"); + char *name = strdup(dirname(dup)); + if (!name) FATAL("no memory left"); + free(dup); + + // search for a matching entry... + size_t i = 0; + while(i < data->n_dirs && strcmp(data->dirs[i].name, name) != 0) { + i++; + } + if (i >= MAX_DIRS) FATAL("too many directories"); + + if (i == data->n_dirs) { // no match found: add + // handover the allocated name to the owning new directory entry + dir_t dir = { .name = name }; + // append the new directory entry + data->dirs[data->n_dirs] = dir; + data->n_dirs++; + } else { + // release the name since match found, and therefore, no need to keep the allocated name anymore + free(name); + } + return i; +} + +/** + * @brief Add a file entry from the dependency file to the data structure. + * @param data [INOUT] The data container instance. + * @param path [IN] The path of one file entry from the dependency file. + * @param level [IN] The dependency level of the file entry from the dependency file. + * @remark The sequence of entries in the dependency file is relevant - it implies direct dependencies. + */ +static void add_file(data_t *data, const char *path, size_t level) +{ + assert(data); + assert(path); + // The function basename() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling basename(). + // Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value. + char *dup = strdup(path); + if (!dup) FATAL("no memory left"); + char *name = strdup(basename(dup)); + if (!name) FATAL("no memory left"); + free(dup); + + if (data->n_files >= MAX_FILES) FATAL("too many files"); + // produce a file entry + file_t file = { .name = name, .dir = get_or_add_dir(data, path), .level = level }; + data->files[data->n_files] = file; + data->n_files++; +} + +/** + * @brief Processes one dependency line of the dependency file. + * @param data [INOUT] The data container instance. + * @param line [IN] The line to parse and store in data. + */ +static void process_line(data_t *data, const char line[]) +{ + assert(data); + + size_t len = strlen(line); + assert(len > 0); + assert(line[0] == '.'); + + // read level + size_t i = strspn(line, "."); + size_t level = i; + // skip spaces + i += strspn(line+i, " \t"); + // take rest as path and add the file to the records + add_file(data, line+i, level); +} + +/* + * The public interface. + */ +const data_t data_read_all(const char *root) +{ + data_t data; + init(&data); + // add as first file the root for the given dependencies + add_file(&data, root, 0); + + char line[MAX_LINE_LEN] = { 0 }; + + // read all stdin line and only process dependency lines (those starting on a '.') + clearerr(stdin); + while(fgets(line, MAX_LINE_LEN, stdin)) { + size_t len = strlen(line); + if (len > 0 && line[len-1] == '\n' && line[0] == '.') { // only dependency lines + line[len-1] = '\0'; + process_line(&data, line); + } + } + return data; +} diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h new file mode 100644 index 0000000..ab1880b --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h @@ -0,0 +1,72 @@ +/** + * @file + * @brief Access to the GCC produced dependency data (via -H command line option). + */ + +// begin of include guard +// BEGIN-STUDENTS-TO-ADD-CODE +#ifndef _DATA_H_ +#define _DATA_H_ +// END-STUDENTS-TO-ADD-CODE + + +// includes which are needed in this header file +// BEGIN-STUDENTS-TO-ADD-CODE +#include +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief Directory container for file entries of the dependency file. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +typedef struct { + const char *name; ///< @brief the path name of the directory as given by the GCC produced dependency file. +} dir_t; +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief File container for the file entries of the dependency file. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +typedef struct { + const char *name; ///< @brief The base name of the file from the GGC produced dependency file (i.e. the plain name, without any directory path). + size_t dir; ///< @brief The index of the directory entry which represents the path as given by the dependency file. + size_t level; ///< @brief The level as read out from the dependecy file. +} file_t; +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief Overall container for all directories and all files from the dependency file. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +typedef struct { + size_t n_dirs; ///< @brief The number of valid entries in the dirs list. + dir_t *dirs; ///< @brief The list of directories. + size_t n_files; ///< @brief The number of valid entries in the files list. + file_t *files; ///< @brief The list of files from the dependency file (the sequence is relevant to determine the dependencies). +} data_t; +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief Entry function to read the deendency data from stdin. + * @param root [IN] The name of the root file (the deoendency file does not mention the root file, so, it has to be passed from outside). + * @return The container of the read data from stdin. See the documentation on gcc -H for details on the dependencies, etc. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +const data_t data_read_all(const char *root); +// END-STUDENTS-TO-ADD-CODE + + + +// end of include guard +// BEGIN-STUDENTS-TO-ADD-CODE +#endif // _DATA_H_ +// END-STUDENTS-TO-ADD-CODE diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h new file mode 100644 index 0000000..3a38ab6 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h @@ -0,0 +1,17 @@ +/** + * @file + * @brief Error handling convenience functions. + */ +#ifndef _ERROR_H_ +#define _ERROR_H_ + +#include +#include + +/** + * @brief Prints the message to stderr and terminates with EXIT_FAILURE. + * @param MSG [IN] The "..." *string literal* to emit as error - no format parameters nor variables supported. + */ +#define FATAL(MSG) do { fprintf(stderr, "ERROR: %s\n", MSG); exit(EXIT_FAILURE); } while(0) + +#endif // _ERROR_H_ diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c new file mode 100644 index 0000000..8cf35f9 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c @@ -0,0 +1,36 @@ + /* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab P04 dep2dot + */ +#include +#include + +#include "error.h" +#include "data.h" +#include "output.h" + +/** + * @brief main function + * @param argc [in] number of entries in argv + * @param argv [in] program name plus command line arguments + * @returns returns success if valid date is given, failure otherwise + * @remark Prerequisit to convert the resulting DOT file on the shell: sodo apt install graphviz + * @remark Convert: gcc -H ... file.c ... 2>file.dep ; dep2dot file.c file.dot && dot -Tpng file.dot >file.png + */ +int main(int argc, const char *argv[]) +{ + if (argc < 2) FATAL("missing arguments\nusage: dep2dot file.c file.dot # from gcc -H ... file.c ... 2>file.dep\n"); + + output_dot(data_read_all(argv[1])); + + return EXIT_SUCCESS; +} diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c new file mode 100644 index 0000000..3a3d33e --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c @@ -0,0 +1,95 @@ +/** + * @file + * @brief Provides output functions for various file formats. + */ +#include "output.h" +#include +#include +#include + +/** + * @brief Writes the node name of the given file. + * @param file [IN] The file for which to write the node name. + * @remark The dependency data contain duplicates of file entries - the node name must be unique for the path and the *basename* of the files. + */ +static void print_node(file_t file) +{ + printf("\"%s (cluster_c%zd)\"", file.name, file.dir); +} + +/** + * @brief Recursively writes the individual direct dependencies for the file given by curr. + * @param files [IN] The array of all files - the sequence is relevant. + * @param len [IN] The lenght of the files array, i.e. the upper limit for curr values and the subsequent index values. + * @param curr [IN] The index into files for the current root for dependencies: curr -> x, curr -> y, ... + * @return Returns the index into files for the next file to process (i.e. curr value for the next call to this function). + * @remark For a given *curr* file, all following files are with depth level + 1 are direct include files. + * @remark All files with a higher level are *indirect* include files, thus *direct* includes from files processed by recursive calls. + * @remark The list of direct includes to the *curr* file terminates with a level equal of less the the *curr* one (or when the list is exchausted). + */ +static size_t dependencies(file_t files[], size_t len, size_t curr) +{ + assert(curr < len); + size_t level = files[curr].level; + size_t file = curr + 1; + while(file < len && files[file].level > level) { + if (files[file].level == level + 1) { + // Write to stdout " file -> include;\n" where file and include are the DOT node names of the respective files + // BEGIN-STUDENTS-TO-ADD-CODE + printf(" "); + print_node(files[curr]); + printf(" -> "); + print_node(files[file]); + printf(";\n"); + // END-STUDENTS-TO-ADD-CODE + file = dependencies(files, len, file); + } else { + file++; + } + } + return file; +} + +/* + * Public interface + */ +void output_dot(const data_t data) +{ + printf("digraph dep {\n"); + // nodes + printf(" node [shape=box]\n"); + for (size_t file = 0; file < data.n_files; file++) { + // Write to stdout " file [label=\"name\"];\n" where file is the DOT node name and name is the file name + // BEGIN-STUDENTS-TO-ADD-CODE + printf(" "); + print_node(data.files[file]); + printf(" [label=\"%s\"];\n", data.files[file].name); + // END-STUDENTS-TO-ADD-CODE + } + // directory clusters + for (size_t dir = 0; dir < data.n_dirs; dir++) { + printf(" subgraph cluster_c%zd {\n", dir); + printf(" label=\"%s\"; %s\n", data.dirs[dir].name, strncmp(data.dirs[dir].name, "/usr/", 5) == 0 ? "style=filled; color=lightgrey;" : "color=black;"); + for (size_t file = 0; file < data.n_files; file++) { + if (data.files[file].dir == dir) { + // Write to stdout " file;\n" where file is the DOT node name + // BEGIN-STUDENTS-TO-ADD-CODE + printf(" "); + print_node(data.files[file]); + printf(";\n"); + // END-STUDENTS-TO-ADD-CODE + } + } + printf(" }\n"); + } + + // dependencies + size_t curr = 0; + do { + curr = dependencies(data.files, data.n_files, curr); + } while(curr < data.n_files); + + printf("}\n"); +} + + diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h new file mode 100644 index 0000000..e0a1b1e --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h @@ -0,0 +1,20 @@ +/** + * @file + * @brief Provides output functions for various file formats. + */ +// define proper header file here, with include gaurd, etc. +// BEGIN-STUDENTS-TO-ADD-CODE +#ifndef _OUTPUT_H_ +#define _OUTPUT_H_ + +#include "data.h" + +/** + * @brief Produces DOT output of the dependencies given in data. + * @param data [IN] Container of the dependenciy data. + */ +void output_dot(const data_t data); + + +#endif // _OUTPUT_H_ +// END-STUDENTS-TO-ADD-CODE diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input new file mode 100644 index 0000000..d21a54c --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input @@ -0,0 +1,7 @@ +Test File +. dir1/h1_1 +.. dir1/h1_1_2 +. dir1/h1_2 +. dir2/h2_1 +.. dir1/h1_1 +Done diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input new file mode 100644 index 0000000..6565e2d --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input @@ -0,0 +1,2 @@ +Test File +Done diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c new file mode 100644 index 0000000..119800f --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c @@ -0,0 +1,140 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Test suite for the given package. + */ +#include +#include +#include +#include +#include +#include +#include "test_utils.h" + +#ifndef TARGET // must be given by the make file --> see test target +#error missing TARGET define +#endif + +/// @brief alias for EXIT_SUCCESS +#define OK EXIT_SUCCESS +/// @brief alias for EXIT_FAILURE +#define FAIL EXIT_FAILURE + +/// @brief The name of the STDOUT text file. +#define OUTFILE "stdout.txt" +/// @brief The name of the STDERR text file. +#define ERRFILE "stderr.txt" + +/// @brief test data file +#define IN_NO_DEP "no_dep.input" +/// @brief test data file +#define IN_DEP "dep.input" + +// setup & cleanup +static int setup(void) +{ + remove_file_if_exists(OUTFILE); + remove_file_if_exists(ERRFILE); + return 0; // success +} + +static int teardown(void) +{ + // Do nothing. + // Especially: do not remove result files - they are removed in int setup(void) *before* running a test. + return 0; // success +} + +// tests +static void test_fail_no_arg(void) +{ + // arrange & act & assert + CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), FAIL); +} + +static void test_no_dep(void) +{ + // arrange + const char *out_txt[] = { + "digraph dep {\n", + " node [shape=box]\n", + " \"root (cluster_c0)\" [label=\"root\"];\n", + " subgraph cluster_c0 {\n", + " label=\".\"; color=black;\n", + " \"root (cluster_c0)\";\n", + " }\n", + "}\n", + }; + + // act & assert + CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_NO_DEP " >" OUTFILE " 2>" ERRFILE)), OK); + + // assert + + assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt)); +} + +static void test_dep(void) +{ + // arrange + const char *out_txt[] = { + "digraph dep {\n", + " node [shape=box]\n", + " \"root (cluster_c0)\" [label=\"root\"];\n", + " \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n", + " \"h1_1_2 (cluster_c1)\" [label=\"h1_1_2\"];\n", + " \"h1_2 (cluster_c1)\" [label=\"h1_2\"];\n", + " \"h2_1 (cluster_c2)\" [label=\"h2_1\"];\n", + " \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n", + " subgraph cluster_c0 {\n", + " label=\".\"; color=black;\n", + " \"root (cluster_c0)\";\n", + " }\n", + " subgraph cluster_c1 {\n", + " label=\"dir1\"; color=black;\n", + " \"h1_1 (cluster_c1)\";\n", + " \"h1_1_2 (cluster_c1)\";\n", + " \"h1_2 (cluster_c1)\";\n", + " \"h1_1 (cluster_c1)\";\n", + " }\n", + " subgraph cluster_c2 {\n", + " label=\"dir2\"; color=black;\n", + " \"h2_1 (cluster_c2)\";\n", + " }\n", + " \"root (cluster_c0)\" -> \"h1_1 (cluster_c1)\";\n", + " \"h1_1 (cluster_c1)\" -> \"h1_1_2 (cluster_c1)\";\n", + " \"root (cluster_c0)\" -> \"h1_2 (cluster_c1)\";\n", + " \"root (cluster_c0)\" -> \"h2_1 (cluster_c2)\";\n", + " \"h2_1 (cluster_c2)\" -> \"h1_1 (cluster_c1)\";\n", + "}\n", + }; + + // act & assert + CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_DEP " >" OUTFILE " 2>" ERRFILE)), OK); + + // assert + + assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt)); +} + +/** + * @brief Registers and runs the tests. + * @returns success (0) or one of the CU_ErrorCode (>0) + */ +int main(void) +{ + // setup, run, teardown + TestMainBasic("lab test", setup, teardown + , test_fail_no_arg + , test_no_dep + , test_dep + ); +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/Makefile b/P05_TicTacToe/work/tic-tac-toe-solution/Makefile new file mode 100644 index 0000000..a29984f --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/Makefile @@ -0,0 +1,9 @@ +SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk") + +TARGET := bin/tic-tac-toe +MODULES := src/model.c src/view.c src/control.c +SOURCES := src/main.c $(MODULES) +TSTSOURCES := tests/tests.c $(MODULES) + +include $(SNP_SHARED_MAKEFILE) + diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox b/P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox new file mode 100644 index 0000000..b07e65d --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P05 Tic Tac Toe Game + * + * @section Purpose + * + * This is a lab on usage of arrays. + * + */ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/control.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.c new file mode 100644 index 0000000..ccae643 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.c @@ -0,0 +1,154 @@ +/** + * @file + * @brief Implementation + */ +#include "control.h" +#include "model.h" +#include + +/** + * @brief Conversion from control field number (1...9) to 0-based model position (row, col). + * @param cell [IN] Field number (1...9). + * @return Returns the position of the field. + * @remark Asserts proper field range. + */ +static model_pos_t get_pos(size_t cell) +{ + assert(1 <= cell && cell <= 9); + model_pos_t pos = { (cell - 1) / 3, (cell - 1) % 3 }; + return pos; +} + +/** + * @brief Conversion from control player to model state. + * @param player [IN] Control player value to convert. + * @return Returns the matching model state. + * @remark No assertion is done - defaults to model_state_none. + */ +static model_state_t get_state(control_player_t player) +{ + switch(player) { + case control_player_a: return model_state_a; + case control_player_b: return model_state_b; + default: return model_state_none; + } +} + +/** + * @brief Conversion from 0-based model position (row, col) to control field number (1...9). + * @param pos [IN] 0-based model position (row,col). + * @return The control filed number (1...9) + * @remark Asserts proper position range. + */ +static size_t get_cell(model_pos_t pos) +{ + assert(pos.row < 3); + assert(pos.col < 3); + return 1 + pos.row * 3 + pos.col; +} + +/** + * @brief Conversion from model state to control player. + * @param state [IN] Model state to convert + * @return Returns the matching control player value. + * @remark No assertion is done - defaults to control_no_player. + */ +static control_player_t get_player(model_state_t state) +{ + switch(state) { + case model_state_a: return control_player_a; + case model_state_b: return control_player_b; + default: return control_no_player; + } +} + +/** + * @brief Queries if a move is possible. + * @param instance [INOUT] The instance which holds the state. + * @return Returns 0 if no move is possible any more, otherwise 1. + */ +static int control_can_move(control_t *instance) +{ + assert(instance); + return model_can_move(instance->model); +} + +// public API function which is documented in the header file. +void control_init(control_t *instance, model_t *model) +{ + assert(instance); + assert(model); + instance->player = control_player_a; + instance->model = model; +} + +// public API function which is documented in the header file. +void control_move(control_t *instance, size_t cell) +{ + assert(instance); + if (model_move(instance->model, get_pos(cell), get_state(instance->player))) { + if (control_can_move(instance)) { + switch(instance->player) { + case control_player_a: + instance->player = control_player_b; + break; + case control_player_b: + instance->player = control_player_a; + break; + default: + break; + } + } else { + instance->player = control_no_player; + } + } +} + +// public API function which is documented in the header file. +control_player_t control_get_winner(control_t *instance) +{ + assert(instance); + return get_player(model_get_winner(instance->model)); +} + +// public API function which is documented in the header file. +control_player_t control_get_player(control_t *instance) +{ + assert(instance); + return instance->player; +} + +// public API function which is documented in the header file. +control_player_t control_get_state(control_t *instance, size_t cell) +{ + assert(instance); + return get_player(model_get_state(instance->model, get_pos(cell))); +} + +// public API function which is documented in the header file. +control_line_t control_get_win(control_t *instance) +{ + assert(instance); + if (control_get_winner(instance) == control_no_player) { + control_line_t no_win = { 0 }; + return no_win; + } + + model_line_t line = model_get_win_line(instance->model); + assert(line.dir != model_dir_none); + size_t start_cell = get_cell(line.start); + switch(line.dir) { + case model_dir_h: + return (control_line_t) { { start_cell, start_cell + 1, start_cell + 2 } }; + case model_dir_v: + return (control_line_t) { { start_cell, start_cell + 3, start_cell + 6 } }; + case model_dir_d: + if (start_cell == 1) { + return (control_line_t) { { start_cell, start_cell + 4, start_cell + 8 } }; + } else { + return (control_line_t) { { start_cell, start_cell + 2, start_cell + 6 } }; + } + default: + return (control_line_t) { { 1, 1, 1 } }; + } +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/control.h b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.h new file mode 100644 index 0000000..613bd8f --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.h @@ -0,0 +1,80 @@ +/** + * @file + * @brief MVC - agent between model and view + */ +#ifndef _CONTROL_H_ +#define _CONTROL_H_ + +#include "model.h" + +/** + * @brief The selection of possible players. + */ +typedef enum { + control_no_player, ///< none of the players + control_player_a, ///< first player + control_player_b, ///< second player +} control_player_t; + +/** + * @brief Sequence of winning cell numbers in increasing cell numbers. + */ +typedef struct { + size_t line[3]; ///< the sequence of cells (1...9) or 0 in the first element if no win +} control_line_t; + +/** + * @brief The instance type. + */ +typedef struct { + control_player_t player; ///< the current player + model_t *model; ///< the reference to the model +} control_t; + +/** + * @brief Constructor: initialize the instance memory. + * @param instance [INOUT] The instance which holds the state. + * @param model [IN] Dependency Injection of the model instance. + */ +void control_init(control_t *instance, model_t *model); + +/** + * @brief Performs a move on the board. + * @param instance [INOUT] The instance which holds the state. + * @param cell [IN] The affected field (1...9) + * @remark Silently ignores a move if it is not allowed (e.g. if already completed or the field is already played, etc.). + */ +void control_move(control_t *instance, size_t cell); + +/** + * @brief Queries the winning player. + * @param instance [INOUT] The instance which holds the state. + * @returns Returns the winning player (if any). + */ +control_player_t control_get_winner(control_t *instance); + +/** + * @brief Queries the next player. + * @param instance [INOUT] The instance which holds the state. + * @returns Returns the next player (if any). + * @remark This is updated by the control_move() function. + */ +control_player_t control_get_player(control_t *instance); + +/** + * @brief Queries the state of a field. + * @param instance [INOUT] The instance which holds the state. + * @param cell [IN] The affected field of the board (1...9). + * @returns Returns the player which played this field (if any). + */ +control_player_t control_get_state(control_t *instance, size_t cell); + +/** + * @brief Gets the winning fields (if any). + * @param instance [INOUT] The instance which holds the state. + * @returns Returns the field numbers in increasing order (1...9) which win the game (if any). + * @remark If there is no winner (yet), the first entry in the result is 0. + */ +control_line_t control_get_win(control_t *instance); + +#endif // _CONTROL_H_ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/main.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/main.c new file mode 100644 index 0000000..7e66b7e --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/main.c @@ -0,0 +1,39 @@ + /* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab P04 dep2dot + */ +#include +#include + +#include "view.h" +#include "model.h" +#include "control.h" + +/** + * @brief main function + * @param argc [in] number of entries in argv + * @param argv [in] program name plus command line arguments + * @returns returns success if valid date is given, failure otherwise + */ +int main(int argc, const char *argv[]) +{ + view_t view; + control_t control; + model_t model; + + model_init(&model); + control_init(&control, &model); + view_init(&view, &control); + view_run(&view); + + return EXIT_SUCCESS; +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/model.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.c new file mode 100644 index 0000000..b544f17 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.c @@ -0,0 +1,154 @@ +/** + * @file + * @brief Implementation + */ +#include "model.h" +#include + +/** + * @brief Asserts that the position is in range. + * @param [IN] The position to check. + */ +static void assert_pos(model_pos_t pos) +{ + assert(pos.row < MODEL_SIZE); + assert(pos.col < MODEL_SIZE); +} + +/** + * @brief Sets the field on the board to the given state. + * @param instance [INOUT] The instance which holds the state. + * @param pos [IN] The affected field. + * @param state [IN] The new state of the field. + */ +static void set_state(model_t *instance, model_pos_t pos, model_state_t state) +{ + assert_pos(pos); + + // Instructions to the students: + // set the field of the board to the new state + // BEGIN-STUDENTS-TO-ADD-CODE + instance->board[pos.row][pos.col] = state; + // END-STUDENTS-TO-ADD-CODE +} + +// public API function which is documented in the header file. +model_pos_t model_pos(size_t row, size_t col) +{ + return (model_pos_t){row, col}; +} + +// public API function which is documented in the header file. +void model_init(model_t *instance) +{ + assert(instance); + + // Instructions to the students: + // set all fields of the board to model_state_none + // BEGIN-STUDENTS-TO-ADD-CODE + for(size_t row = 0; row < MODEL_SIZE; row++) { + for(size_t col = 0; col < MODEL_SIZE; col++) { + instance->board[row][col] = model_state_none; + } + } + // END-STUDENTS-TO-ADD-CODE +} + +// public API function which is documented in the header file. +model_state_t model_get_state(model_t *instance, model_pos_t pos) +{ + assert(instance); + assert_pos(pos); + + // Instructions to the students: + // replace the stub implementation my access to the field at the given position. + // BEGIN-STUDENTS-TO-ADD-CODE + return instance->board[pos.row][pos.col]; // stub return model_state_none; + // END-STUDENTS-TO-ADD-CODE +} + +// public API function which is documented in the header file. +model_line_t model_get_win_line(model_t *instance) +{ + assert(instance); + model_state_t anchor; + + // horizontal + for(size_t row = 0; row < MODEL_SIZE; row++) { + anchor = model_get_state(instance, model_pos(row, 0)); + if (anchor != model_state_none + && anchor == model_get_state(instance, model_pos(row, 1)) + && anchor == model_get_state(instance, model_pos(row, 2))) { + return (model_line_t) { model_dir_h, { row, 0 } }; + } + } + + // vertical + for(size_t col = 0; col < MODEL_SIZE; col++) { + anchor = model_get_state(instance, model_pos(0, col)); + if (anchor != model_state_none + && anchor == model_get_state(instance, model_pos(1, col)) + && anchor == model_get_state(instance, model_pos(2, col))) { + return (model_line_t) { model_dir_v, { 0, col } }; + } + + + } + + // diagonal + anchor = model_get_state(instance, model_pos(1, 1)); + if (anchor != model_state_none) { + if (anchor == model_get_state(instance, model_pos(0, 0)) && anchor == model_get_state(instance, model_pos(2, 2))) { + return (model_line_t) { model_dir_d, { 0, 0 } }; + } + if (anchor == model_get_state(instance, model_pos(2, 0)) && anchor == model_get_state(instance, model_pos(0, 2))) { + return (model_line_t) { model_dir_d, { 0, 2 } }; + } + } + + // fallback + return (model_line_t) { model_dir_none, { 0, 0 } }; +} + +// public API function which is documented in the header file. +model_state_t model_get_winner(model_t *instance) +{ + assert(instance); + model_line_t line = model_get_win_line(instance); + return line.dir == model_dir_none + ? model_state_none + : model_get_state(instance, model_pos(line.start.row, line.start.col)) + ; +} + +// public API function which is documented in the header file. +int model_can_move(model_t *instance) +{ + assert(instance); + if (model_get_winner(instance) == model_state_none) { + // Instructions to the students: + // scan all fields: return 1 with first field which equals model_state_none + // BEGIN-STUDENTS-TO-ADD-CODE + for(size_t row = 0; row < MODEL_SIZE; row++) { + for (size_t col = 0; col < MODEL_SIZE; col++) { + if (instance->board[row][col] == model_state_none) { + return 1; + } + } + } + // END-STUDENTS-TO-ADD-CODE + } + return 0; +} + +// public API function which is documented in the header file. +int model_move(model_t *instance, model_pos_t pos, model_state_t state) +{ + assert(instance); + assert_pos(pos); + if (model_get_state(instance, pos) == model_state_none && model_can_move(instance)) { + set_state(instance, pos, state); + return 1; + } + return 0; +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/model.h b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.h new file mode 100644 index 0000000..51e2678 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.h @@ -0,0 +1,108 @@ +/** + * @file + * @brief MVC - Model instance + */ +#ifndef _MODEL_H_ +#define _MODEL_H_ + +#include + +#define MODEL_SIZE 3 ///< size of the game to avoid magic numbers in the code (not meant to modify) + +/** + * @brief The position on the board. + */ +typedef struct { + size_t row; ///< The row (0-based). + size_t col; ///< The column (0-based). +} model_pos_t; + +/** + * @brief Winner line direction - the winner line is given together with the start position. + */ +typedef enum { + model_dir_none, ///< no winner line + model_dir_h, ///< horizontal + model_dir_v, ///< vertical + model_dir_d, ///< diagonal +} model_dir_t; + +/** + * @brief The Winner line (if any). + */ +typedef struct { + model_dir_t dir; ///< the winner line direction (if any) + model_pos_t start; ///< the start position of the winner line +} model_line_t; + +/** + * @brief The state of a field. + */ +typedef enum { + model_state_none, ///< field available to play + model_state_a, ///< field already played + model_state_b, ///< field already played +} model_state_t; + +/** + * @brief The instance type. + */ +typedef struct { + model_state_t board[MODEL_SIZE][MODEL_SIZE]; ///< the play board +} model_t; + +/** + * @brief Convert to row and col to position. + * @param row [IN] position parameter + * @param col [IN] position parameter + * @return Returns the position given be row and col parameters. + */ +model_pos_t model_pos(size_t row, size_t col); + +/** + * @brief Constructor: initialize the instance memory. + * @param instance [INOUT] The instance which holds the state. + */ +void model_init(model_t *instance); + +/** + * @brief Queries the state of the given field. + * @param instance [INOUT] The instance which holds the state. + * @param pos [IN] The affected field. + * @return Returns the state of the field. + */ +model_state_t model_get_state(model_t *instance, model_pos_t pos); + +/** + * @brief Queries the winner (if any). + * @param instance [INOUT] The instance which holds the state. + * @return Returns the wining player or model_state_none if no winner (yet). + */ +model_state_t model_get_winner(model_t *instance); + +/** + * @brief Queries if a move is possible (i.e. not won yet and any field available?). + * @param instance [INOUT] The instance which holds the state. + * @return Returns 0 if no move possible, 1 otherwise. + */ +int model_can_move(model_t *instance); + +/** + * @brief Do a move if possible. + * @param instance [INOUT] The instance which holds the state. + * @param pos [IN] The field to play. + * @param state [IN] The new state (only model_state_a and model_state_b allowed). + * @return Returns if the attempt to move was successful. + * @remark Does only succeed if not yet won and if the field is available. + */ +int model_move(model_t *instance, model_pos_t pos, model_state_t state); + +/** + * @brief Gets the winner line (if any). + * @param instance [INOUT] The instance which holds the state. + * @returns The line which wins (if any). + * @remark The start position is 0/0, 1/0, 2/0 for horizontal, 0/0, 0/1, 0/2 for vertical, 0/0, 0/2 for diagonal. + */ +model_line_t model_get_win_line(model_t *instance); + +#endif // _MODEL_H_ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/view.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.c new file mode 100644 index 0000000..a248c49 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.c @@ -0,0 +1,255 @@ +/** + * @file + * @brief Implementation + */ +#include "view.h" +#include "control.h" + +#include // assert() +#include // various i/o +#include // isdigit() +#include // STDIN_FILENO, isatty() +#include // tcgetattr(), tcsetattr() + +#define EXIT '0' ///< the UI exit request + +#define CLS "\033[2J" ///< ANSI termial CSI sequence for clear screen +#define AVAILABLE "\033[40m" ///< ANSI termial CSI sequence for available fields (black) +#define PLAYER_A "\033[42m" ///< ANSI termial CSI sequence for one player (green) +#define PLAYER_B "\033[41m" ///< ANSI termial CSI sequence for the other player (red) +#define GAP "\033[47m" ///< ANSI termial CSI sequence for boarder (white) +#define RESET "\033[0m" ///< ANSI termial CSI sequence to reset all settings + +#define CELL_WIDTH 10 ///< rendering parameter: columns per cell +#define CELL_HEIGHT 5 ///< rendering parameter: rows per cell +#define GAP_WIDTH 4 ///< rendering parameter: columns per border +#define GAP_HEIGHT 2 ///< rendering parameter: rows per boarder + +#define SIZE 3 ///< size of the game to avoid magic numbers in the code (not meant to modify) +#define CELLS (SIZE * SIZE) ///< size of the game to avoid magic numbers in the code (not meant to modify) + +/** + * @brief Position the cursor for further output. + * @param row [IN] position parameter + * @param col [IN] position parameter + */ +static void goto_pos(size_t row, size_t col) +{ + printf("\033[%zd;%zdH", row, col); +} + +/** + * @brief Displays a sequence of spaces at the given position in the given background color. + * @param row [IN] position parameter + * @param col [IN] position parameter + * @param width [IN] how many spaces to write + * @param color [IN] the format string before writing the spaces (intent: background color) + * @remark After writing the spaces, the format is reset. + */ +static size_t show_bar(size_t row, size_t col, size_t width, const char* color) +{ + goto_pos(row, col); + printf("%s", color); + for(size_t col = 0; col < width; col++) { + putchar(' '); + } + printf(RESET); + return col + width; +} + +/** + * @brief Displays a horizontal border over the whole board width. + * @param row [IN] position parameter + * @param col [IN] position parameter + */ +static size_t show_h_gap(size_t row, size_t col) { + for(size_t i = 0; i < GAP_HEIGHT; i++) { + show_bar(row+i, col, GAP_WIDTH + CELL_WIDTH + GAP_WIDTH, GAP); + } + return row + GAP_HEIGHT; +} + +/** + * @brief Writes for the call at position y/x the given number with the given background color. + * @param y [IN] position parameter: the upper left row of the cell + * @param x [IN] position parameter: the upper left column of the cell + * @param n [IN] the number to write as text + * @param color [IN] the format string before writing the text (intent: background color) + * @remark After writing the number, the format is reset. + */ +static void show_cell_nr(size_t y, size_t x, size_t n, const char *color) +{ + size_t cy = (y + y + CELL_HEIGHT)/2; + size_t cx = (x + x + CELL_WIDTH - 2)/2; + + goto_pos(cy, cx); + printf("%s", color); + printf("%2zd", n); + printf(RESET); +} + +/** + * @brief Renders the given cell with the given background color, including the surrounding border. + * @param n [IN] the cell number (0...CELLS-1) + * @param color [IN] the format string for the cell content (intent: background color) + * @remark After writing the number, the format is reset. + */ +static void show_cell(size_t n, const char *color) +{ + // goto upper-left corner of a cell (the cell starts with an upper and left gap) + size_t y = 1 + n / SIZE * (GAP_HEIGHT + CELL_HEIGHT); + size_t x = 1 + n % SIZE * (GAP_WIDTH + CELL_WIDTH); + + size_t row = show_h_gap(y, x); + for(size_t i = 0; i < CELL_HEIGHT; i++) { + size_t col = x; + col = show_bar(row, col, GAP_WIDTH, GAP); + col = show_bar(row, col, CELL_WIDTH, color); + col = show_bar(row, col, GAP_WIDTH, GAP); + row++; + } + row = show_h_gap(row, x); + show_cell_nr(y + GAP_HEIGHT, x + GAP_WIDTH, n + 1, color); + goto_pos(row, 0); +} + +/** + * @brief Renders the given player's name in the given background color. + * @param player [IN] the player to render (select the background color and the name of the player + * @remark After writing the content, the format is reset. + */ +static void print_player(control_player_t player) +{ + switch(player) { + case control_player_a: + printf(PLAYER_A); + printf("Player A"); + printf(RESET); + break; + case control_player_b: + printf(PLAYER_B); + printf("Player B"); + printf(RESET); + break; + default: + printf(RESET); + printf("none"); + break; + } +} + +/** + * @brief Displays a label followed by the given player. + * @param row [IN] position parameter + * @param col [IN] position parameter + * @param label [IN] the profixing label + * @param player [IN] the player to display + */ +static void show_player(size_t row, size_t col, const char *label, control_player_t player) +{ + goto_pos(row, col); + printf(RESET); + col += printf("%s", label); + goto_pos(row, col); + print_player(player); +} + +/** + * @brief Renders the winner and the next player. + * @param winner [IN] the winning player (if any) + * @param next [IN] the next player (if any) + */ +static void show_status(control_player_t winner, control_player_t next) +{ + size_t y = GAP_HEIGHT; + size_t x = SIZE * (GAP_WIDTH + CELL_WIDTH) + GAP_WIDTH + GAP_WIDTH; + size_t row = y; + size_t col = x; + + show_player(row, col, "Winner is: ", winner); + row += 2; + show_player(row, col, "Next player is: ", next); + row += 2; + row += 2; + goto_pos(row, col); + printf("0: exit"); + row += 2; + goto_pos(row, col); + printf("1..9: play field"); +} + +/** + * @brief Renders the board from the status given by the control instance. + * @param instance [IN] the instance which holds the control instance + */ +static void show(view_t *instance) +{ + assert(instance); + assert(instance->control); + puts(CLS); + show_status(control_get_winner(instance->control), control_get_player(instance->control)); + + for(size_t i = 0; i < CELLS; i++) { + const char *color = AVAILABLE; + switch(control_get_state(instance->control, i+1)) { + case control_player_a: + color = PLAYER_A; + break; + case control_player_b: + color = PLAYER_B; + break; + default: + break; + } + show_cell(i, color); + } +} + +/** + * @brief Processes the input and dsiplays the result. + * @param the instance which holds the control instance + */ +static void notifier_loop(view_t *instance) +{ + show(instance); + int c = getchar(); + while(c != EOF && c != EXIT) { + if (isdigit(c)) { + control_move(instance->control, c-'0'); + } + show(instance); + c = getchar(); + } +} + + +// public API function which is documented in the header file. +void view_init(view_t *instance, control_t *control) +{ + assert(instance); + assert(control); + instance->control = control; +} + +// public API function which is documented in the header file. +void view_run(view_t *instance) +{ + if (isatty(STDIN_FILENO)) { // in case of an interactive terminal, the exhoing and buffering is disabled + // declare non POSIX function, which is available in glibc, but not in strinct C99 mode + void cfmakeraw(struct termios *termios_p); + + // replace original tty IO state... + struct termios orig; + struct termios raw; + cfmakeraw(&raw); + tcgetattr(STDIN_FILENO, &orig); + tcsetattr(STDIN_FILENO, TCSANOW, &raw); + // ...do the work... + notifier_loop(instance); + // ...and finalle restore original tty IO state + tcsetattr(STDIN_FILENO, TCSANOW, &orig); + } else { // if not an interactive terminal, no tweaking with IO is done + notifier_loop(instance); + } +} + diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/view.h b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.h new file mode 100644 index 0000000..84cee90 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.h @@ -0,0 +1,31 @@ +/** + * @file + * @brief MVC - View instance + */ +#ifndef _VIEW_H_ +#define _VIEW_H_ + +#include "control.h" + +/** + * @brief The instance type. + */ +typedef struct { + control_t *control; ///< the instance knows of the control instance +} view_t; + +/** + * @brief Constructor: initialize the instance memory. + * @param instance [INOUT] The instance which holds the state. + * @param control [IN] Dependency Injection of the control instance. + */ +void view_init(view_t *instance, control_t *control); + +/** + * @brief Starts the notifyer loop: accepts input and displays the results. + * @param instance [INOUT] The instance which holds the state. + * @remark Does only return when termination is requested through the UI. + */ +void view_run(view_t *instance); + +#endif // _VIEW_H_ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c b/P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c new file mode 100644 index 0000000..f2d15df --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c @@ -0,0 +1,445 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Test suite for the given package. + */ +#include +#include +#include +#include +#include +#include +#include "test_utils.h" +#include "model.h" + +#ifndef TARGET // must be given by the make file --> see test target +#error missing TARGET define +#endif + +/// @brief alias for EXIT_SUCCESS +#define OK EXIT_SUCCESS +/// @brief alias for EXIT_FAILURE +#define FAIL EXIT_FAILURE + +/// @brief The name of the STDOUT text file. +#define OUTFILE "stdout.txt" +/// @brief The name of the STDERR text file. +#define ERRFILE "stderr.txt" + +#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error + +// setup & cleanup +static int setup(void) +{ + remove_file_if_exists(OUTFILE); + remove_file_if_exists(ERRFILE); + return 0; // success +} + +static int teardown(void) +{ + // Do nothing. + // Especially: do not remove result files - they are removed in int setup(void) *before* running a test. + return 0; // success +} + +// test utils +static void init_model(model_t *instance, int act) +{ + if (act) printf(TRACE_INDENT "init_model:... "); + model_init(instance); + for(size_t row = 0; row < MODEL_SIZE; row++) { + for(size_t col = 0; col < MODEL_SIZE; col++) { + if (act) printf("%zd/%zd ", row, col); + CU_ASSERT_EQUAL_FATAL(instance->board[row][col], model_state_none); + } + } + if (act) printf(TRACE_INDENT); +} + +static void print_board(model_state_t board[MODEL_SIZE][MODEL_SIZE]) +{ + for(size_t row = 0; row < MODEL_SIZE; row++) { + printf("{ "); + for(size_t col = 0; col < MODEL_SIZE; col++) { + printf("%d ", board[row][col]); + } + printf("} "); + } +} + + +// tests +static void test_model_init(void) +{ + // check void model_init(model_t *instance); + + // arrange + model_t model; + // act & assert + init_model(&model, 1); +} + +static void test_model_get_state(void) +{ + // check: model_state_t model_get_state(model_t *instance, model_pos_t pos); + + { + // arrange + model_t model; + init_model(&model, 0); + // act & assert + printf(TRACE_INDENT "initial state:... "); + print_board(model.board); + for(size_t row = 0; row < MODEL_SIZE; row++) { + for(size_t col = 0; col < MODEL_SIZE; col++) { + printf("%zd/%zd ", row, col); + CU_ASSERT_EQUAL_FATAL(model_get_state(&model, model_pos(row, col)), model_state_none); + } + } + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_b }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "modified state:... "); + print_board(model.board); + for(size_t row = 0; row < MODEL_SIZE; row++) { + for(size_t col = 0; col < MODEL_SIZE; col++) { + printf("%zd/%zd ", row, col); + CU_ASSERT_EQUAL_FATAL(model_get_state(&model, model_pos(row, col)), board[row][col]); + } + } + } + printf(TRACE_INDENT); +} + +static void test_model_get_winner(void) +{ + // check: model_state_t model_get_winner(model_t *instance); + + { + // arrange + model_t model; + init_model(&model, 0); + // act & assert + printf(TRACE_INDENT "initial no winner:... "); + print_board(model.board); + CU_ASSERT_EQUAL_FATAL(model_get_winner(&model), model_state_none); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_b }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "winner:... "); + print_board(model.board); + CU_ASSERT_EQUAL_FATAL(model_get_winner(&model), model_state_b); + } + printf(TRACE_INDENT); +} + +static void test_model_can_move(void) +{ + // check: int model_can_move(model_t *instance); + + { + // arrange + model_t model; + init_model(&model, 0); + // act & assert + printf(TRACE_INDENT "initial can move:... "); + print_board(model.board); + CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 1); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_a }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_b }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "can move while not yet done nor win:... "); + print_board(model.board); + CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 1); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_b }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "cannot move after win:... "); + print_board(model.board); + CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_b, model_state_a, model_state_a }, + { model_state_a, model_state_b, model_state_b }, + { model_state_b, model_state_a, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "cannot move when all done:... "); + print_board(model.board); + CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0); + } + printf(TRACE_INDENT); +} + +static void test_model_move(void) +{ + // check: int model_move(model_t *instance, model_pos_t pos, model_state_t state); + + { + // arrange + model_t model; + init_model(&model, 0); + // act & assert + printf(TRACE_INDENT "initial move:... "); + print_board(model.board); + model_pos_t pos_a = model_pos(0, 0); + printf("%zd/%zd ", pos_a.row, pos_a.col); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_a), 1); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_a), 0); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_b), 0); + model_pos_t pos_b = model_pos(2, 2); + printf("%zd/%zd ", pos_b.row, pos_b.col); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_b), 1); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_b), 0); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_a), 0); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_a }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_b }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "can move while not yet done nor win:... "); + print_board(model.board); + model_pos_t pos = model_pos(2, 1); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 1); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_b }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "cannot move after win:... "); + print_board(model.board); + model_pos_t pos = model_pos(2, 1); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_b, model_state_a, model_state_a }, + { model_state_a, model_state_b, model_state_b }, + { model_state_b, model_state_a, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "cannot move when all done:... "); + print_board(model.board); + for(size_t row = 0; row < MODEL_SIZE; row++) { + for(size_t col = 0; col < MODEL_SIZE; col++) { + model_pos_t pos = model_pos(row, col); + printf("%zd/%zd ", row, col); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0); + CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0); + } + } + CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0); + } + printf(TRACE_INDENT); +} + +static void test_model_get_win_line(void) +{ + // check: model_line_t model_get_win_line(model_t *instance); + + { + // arrange + model_t model; + init_model(&model, 0); + // act & assert + printf(TRACE_INDENT "initial no winner:... "); + print_board(model.board); + model_line_t no_win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_none, model_state_a, model_state_a }, + { model_state_a, model_state_b, model_state_none }, + { model_state_b, model_state_none, model_state_b }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "no winner while not yet done nor win:... "); + print_board(model.board); + model_line_t no_win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none); + } + { + // arrange + static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = { + { model_state_b, model_state_a, model_state_a }, + { model_state_a, model_state_b, model_state_b }, + { model_state_b, model_state_a, model_state_a }, + }; + model_t model; + init_model(&model, 0); + memcpy(model.board, board, sizeof(board)); + + // act & assert + printf(TRACE_INDENT "no winner when all done:... "); + print_board(model.board); + model_line_t no_win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none); + } + { + for(size_t row = 0; row < MODEL_SIZE; row++) { + // arrange + model_t model; + init_model(&model, 0); + for(size_t col = 0; col < MODEL_SIZE; col++) { + CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(row, col), model_state_a), 1); + } + // act & assert + printf(TRACE_INDENT "row winner:... "); + print_board(model.board); + model_line_t win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_h); + CU_ASSERT_EQUAL_FATAL(win.start.row, row); + CU_ASSERT_EQUAL_FATAL(win.start.col, 0); + } + } + { + for(size_t col = 0; col < MODEL_SIZE; col++) { + // arrange + model_t model; + init_model(&model, 0); + for(size_t row = 0; row < MODEL_SIZE; row++) { + CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(row, col), model_state_a), 1); + } + // act & assert + printf(TRACE_INDENT "column winner:... "); + print_board(model.board); + model_line_t win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_v); + CU_ASSERT_EQUAL_FATAL(win.start.row, 0); + CU_ASSERT_EQUAL_FATAL(win.start.col, col); + } + } + { + printf(TRACE_INDENT "diagonal left-right winner:... "); + // arrange + model_t model; + init_model(&model, 0); + for(size_t i = 0; i < MODEL_SIZE; i++) { + CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(i, i), model_state_a), 1); + } + // act & assert + print_board(model.board); + model_line_t win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_d); + CU_ASSERT_EQUAL_FATAL(win.start.row, 0); + CU_ASSERT_EQUAL_FATAL(win.start.col, 0); + } + { + printf(TRACE_INDENT "diagonal right-left winner:... "); + // arrange + model_t model; + init_model(&model, 0); + for(size_t i = 0; i < MODEL_SIZE; i++) { + CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(MODEL_SIZE - 1 - i, i), model_state_a), 1); + } + // act & assert + print_board(model.board); + model_line_t win = model_get_win_line(&model); + CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_d); + CU_ASSERT_EQUAL_FATAL(win.start.row, 0); + CU_ASSERT_EQUAL_FATAL(win.start.col, MODEL_SIZE - 1); + } + printf(TRACE_INDENT); +} + +/** + * @brief Registers and runs the tests. + * @returns success (0) or one of the CU_ErrorCode (>0) + */ +int main(void) +{ + // setup, run, teardown + TestMainBasic("lab test", setup, teardown + , test_model_init + , test_model_get_state + , test_model_get_winner + , test_model_can_move + , test_model_move + , test_model_get_win_line + ); +} diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile new file mode 100644 index 0000000..95121b9 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile @@ -0,0 +1,13 @@ +SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk") + +TARGET := bin/personen-verwaltung +# BEGIN-STUDENTS-TO-ADD-CODE +MODULES := src/person.c src/list.c +# END-STUDENTS-TO-ADD-CODE +SOURCES := src/main.c $(MODULES) +TSTSOURCES := tests/tests.c $(MODULES) + + +include $(SNP_SHARED_MAKEFILE) + +# CFLAGS += -Werror diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox new file mode 100644 index 0000000..05d1c72 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P07 Linked List + * + * @section Purpose + * + * This is a lab on usage of arrays. + * + */ diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c new file mode 100644 index 0000000..49d2d51 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include "list.h" + +static node_t anchor; + +static int is_anchor(const node_t *node) +{ + return node == &anchor; +} + +static void remove_next(node_t *at) +{ + assert(at); + assert(at->next); + if (!is_anchor(at->next)) { + node_t *next = at->next->next; + free(at->next); + at->next = next; + } +} + +static node_t *find_insert(const person_t *p) +{ + assert(p); + node_t *last = &anchor; + for(node_t *n = anchor.next; !is_anchor(n); last = n, n = n->next) { + int res = person_compare(&(n->content), p); + if (res == 0) { + return NULL; // *** EARLY RETURN ***// // already part of the list + } else if (res > 0) { + break; // the predecessor is the insert point + } + } + return last; +} + +static node_t *find_remove(const person_t *p) +{ + assert(p); + node_t *last = &anchor; + for(node_t *n = anchor.next; !is_anchor(n); last = n, n = n->next) { + int res = person_compare(&(n->content), p); + if (res == 0) { + break; // the predecessor is the remove point + } + } + return is_anchor(last->next) ? NULL : last; +} + +const node_t *list_anchor(void) +{ + return &anchor; +} + +const node_t *list_init() +{ + anchor.next = &anchor; + return &anchor; +} + +int list_insert(const person_t *p) +{ + node_t *at = find_insert(p); + node_t *insert = NULL; + if (at) { + insert = malloc(sizeof(node_t)); + if (insert) { + insert->content = *p; + insert->next = at->next; + at->next = insert; + } + } + return at && insert; +} + +int list_remove(const person_t *p) +{ + node_t *at = find_remove(p); + if (at) { + remove_next(at); + } + return at != NULL; +} + +void list_clear(void) +{ + node_t *n = &anchor; + do { + remove_next(n); + } while (!is_anchor(n->next)); +} + +void list_show(void) +{ + node_t *n = &anchor; + do { + if (!is_anchor(n)) printf("%20s %20s %u\n", n->content.name, n->content.first_name, n->content.age); + n = n->next; + } while(!is_anchor(n)); +} + diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h new file mode 100644 index 0000000..3703d97 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h @@ -0,0 +1,17 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +#include "person.h" + +typedef struct node { + person_t content; // in diesem Knoten gespeicherte Person + struct node *next; // Pointer auf den nächsten Knoten in der Liste +} node_t; + +const node_t *list_init(); +int list_insert(const person_t *p); +int list_remove(const person_t *p); +void list_clear(void); +void list_show(void); + +#endif // _LIST_H_ diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c new file mode 100644 index 0000000..77aefb4 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c @@ -0,0 +1,68 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include + +#include "person.h" +#include "list.h" + +/** + * @brief Main entry point. + * @param[in] argc The size of the argv array. + * @param[in] argv The command line arguments... + * @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) there is an expression syntax error. + */ +int main(int argc, char* argv[]) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + list_init(); + person_t p; + int show_menu = 1; + while(1) { + if (show_menu) printf("I(nsert), R(emove), S(how), C(lear), E(nd)\n"); + show_menu = 1; + int op = getchar(); + switch(op) { + case 'I': case 'i': + if (!person_read(&p) || !list_insert(&p)) { + printf("failed to insert person\n"); + } + break; + case 'R': case 'r': + if (!person_read(&p) || !list_remove(&p)) { + printf("failed to remove person\n"); + } + break; + case 'S': case 's': + list_show(); + break; + case 'C': case 'c': + list_clear(); + break; + case EOF: + case 'E': case 'e': + return EXIT_SUCCESS; // *** EARLY RETURN *** // + break; + case ' ': case '\n': + show_menu = 0; + break; + default: + printf("Unknown command: %c\n", op); + break; + } + } + + // END-STUDENTS-TO-ADD-CODE + return EXIT_SUCCESS; +} diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c new file mode 100644 index 0000000..21c39bd --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c @@ -0,0 +1,22 @@ +#include +#include +#include + +#include "person.h" + +int person_compare(const person_t *a, const person_t *b) +{ + assert(a); + assert(b); + int res = strncmp(a->name, b->name, NAME_LEN); + if (res == 0) res = strncmp(a->first_name, b->first_name, NAME_LEN); + if (res == 0) res = a->age - b->age; + return res; +} + +int person_read(person_t *p) +{ + assert(p); + assert(NAME_LEN == 20); + return scanf("%19s %19s %u", p->name, p->first_name, &(p->age)) == 3; +} diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h new file mode 100644 index 0000000..79bd22e --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h @@ -0,0 +1,26 @@ +#ifndef _PERSON_H_ +#define _PERSON_H_ + +#define NAME_LEN 20 + +typedef struct { + char name[NAME_LEN]; + char first_name[NAME_LEN]; + unsigned int age; +} person_t; + +/** + * @brief Compares two persons in this sequence: 1st=name, 2nd=first_name, 3rd=age + * @param a [IN] const reference to 1st person in the comparison + * @param b [IN] const reference to 2nd person in the comparison + * @return =0 if all record fields are the same + * >0 if all previous fields are the same, but for this field, a is greater + * <0 if all previous fields are the same, but for this field, b is greater + * @remark strncmp() is used for producing the result of string field comparisons + * @remark a->age – b->age is used for producing the result of age comparison + */ +int person_compare(const person_t *a, const person_t *b); + +int person_read(person_t *p); + +#endif // _PERSON_H_ diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c new file mode 100644 index 0000000..addb7e4 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c @@ -0,0 +1,299 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Test suite for the given package. + */ +#include +#include +#include +#include +#include +#include +#include "test_utils.h" +#include "person.h" +#include "list.h" + +#ifndef TARGET // must be given by the make file --> see test target +#error missing TARGET define +#endif + +/// @brief alias for EXIT_SUCCESS +#define OK EXIT_SUCCESS +/// @brief alias for EXIT_FAILURE +#define FAIL EXIT_FAILURE + +/// @brief The name of the STDOUT text file. +#define OUTFILE "stdout.txt" +/// @brief The name of the STDERR text file. +#define ERRFILE "stderr.txt" + +#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error + +// setup & cleanup +static int setup(void) +{ + remove_file_if_exists(OUTFILE); + remove_file_if_exists(ERRFILE); + return 0; // success +} + +static int teardown(void) +{ + // Do nothing. + // Especially: do not remove result files - they are removed in int setup(void) *before* running a test. + return 0; // success +} + +// tests +static void test_person_compare(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + person_t a = { "a", "a", 1 }; + person_t b = { "a", "a", 2 }; + person_t c = { "a", "b", 1 }; + person_t d = { "a", "b", 2 }; + person_t e = { "b", "a", 1 }; + person_t f = { "b", "a", 2 }; + person_t g = { "b", "b", 1 }; + person_t h = { "b", "b", 2 }; + + // act & assert + CU_ASSERT_TRUE(person_compare(&a, &a) == 0); + CU_ASSERT_TRUE(person_compare(&a, &b) < 0); + CU_ASSERT_TRUE(person_compare(&a, &c) < 0); + CU_ASSERT_TRUE(person_compare(&a, &d) < 0); + CU_ASSERT_TRUE(person_compare(&a, &e) < 0); + CU_ASSERT_TRUE(person_compare(&a, &f) < 0); + CU_ASSERT_TRUE(person_compare(&a, &g) < 0); + CU_ASSERT_TRUE(person_compare(&a, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&b, &a) > 0); + CU_ASSERT_TRUE(person_compare(&b, &b) == 0); + CU_ASSERT_TRUE(person_compare(&b, &c) < 0); + CU_ASSERT_TRUE(person_compare(&b, &d) < 0); + CU_ASSERT_TRUE(person_compare(&b, &e) < 0); + CU_ASSERT_TRUE(person_compare(&b, &f) < 0); + CU_ASSERT_TRUE(person_compare(&b, &g) < 0); + CU_ASSERT_TRUE(person_compare(&b, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&c, &a) > 0); + CU_ASSERT_TRUE(person_compare(&c, &b) > 0); + CU_ASSERT_TRUE(person_compare(&c, &c) == 0); + CU_ASSERT_TRUE(person_compare(&c, &d) < 0); + CU_ASSERT_TRUE(person_compare(&c, &e) < 0); + CU_ASSERT_TRUE(person_compare(&c, &f) < 0); + CU_ASSERT_TRUE(person_compare(&c, &g) < 0); + CU_ASSERT_TRUE(person_compare(&c, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&d, &a) > 0); + CU_ASSERT_TRUE(person_compare(&d, &b) > 0); + CU_ASSERT_TRUE(person_compare(&d, &c) > 0); + CU_ASSERT_TRUE(person_compare(&d, &d) == 0); + CU_ASSERT_TRUE(person_compare(&d, &e) < 0); + CU_ASSERT_TRUE(person_compare(&d, &f) < 0); + CU_ASSERT_TRUE(person_compare(&d, &g) < 0); + CU_ASSERT_TRUE(person_compare(&d, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&e, &a) > 0); + CU_ASSERT_TRUE(person_compare(&e, &b) > 0); + CU_ASSERT_TRUE(person_compare(&e, &c) > 0); + CU_ASSERT_TRUE(person_compare(&e, &d) > 0); + CU_ASSERT_TRUE(person_compare(&e, &e) == 0); + CU_ASSERT_TRUE(person_compare(&e, &f) < 0); + CU_ASSERT_TRUE(person_compare(&e, &g) < 0); + CU_ASSERT_TRUE(person_compare(&e, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&f, &a) > 0); + CU_ASSERT_TRUE(person_compare(&f, &b) > 0); + CU_ASSERT_TRUE(person_compare(&f, &c) > 0); + CU_ASSERT_TRUE(person_compare(&f, &d) > 0); + CU_ASSERT_TRUE(person_compare(&f, &e) > 0); + CU_ASSERT_TRUE(person_compare(&f, &f) == 0); + CU_ASSERT_TRUE(person_compare(&f, &g) < 0); + CU_ASSERT_TRUE(person_compare(&f, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&g, &a) > 0); + CU_ASSERT_TRUE(person_compare(&g, &b) > 0); + CU_ASSERT_TRUE(person_compare(&g, &c) > 0); + CU_ASSERT_TRUE(person_compare(&g, &d) > 0); + CU_ASSERT_TRUE(person_compare(&g, &e) > 0); + CU_ASSERT_TRUE(person_compare(&g, &f) > 0); + CU_ASSERT_TRUE(person_compare(&g, &g) == 0); + CU_ASSERT_TRUE(person_compare(&g, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&h, &a) > 0); + CU_ASSERT_TRUE(person_compare(&h, &b) > 0); + CU_ASSERT_TRUE(person_compare(&h, &c) > 0); + CU_ASSERT_TRUE(person_compare(&h, &d) > 0); + CU_ASSERT_TRUE(person_compare(&h, &e) > 0); + CU_ASSERT_TRUE(person_compare(&h, &f) > 0); + CU_ASSERT_TRUE(person_compare(&h, &g) > 0); + CU_ASSERT_TRUE(person_compare(&h, &h) == 0); + + // END-STUDENTS-TO-ADD-CODE +} + +static void test_list_insert(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + const node_t *anchor = list_init(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: insert one + person_t p1 = { "a", "b", 123 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + + // act & assert: insert a second after first + person_t p2 = { "a", "b", 124 }; + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + + // act & assert: insert a second before first + person_t p3 = { "a", "b", 122 }; + CU_ASSERT_TRUE(list_insert(&p3)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p3) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->next->content), &p2) == 0); + + // act & assert: reject inserting same + CU_ASSERT_FALSE(list_insert(&p1)); + // unchanged + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next->next); + // unchanged + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p3) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->next->content), &p2) == 0); + + // END-STUDENTS-TO-ADD-CODE +} + +static void test_list_remove(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + const node_t *anchor = list_init(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: remove one + person_t p1 = { "a", "b", 123 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + // remove same + CU_ASSERT_TRUE_FATAL(list_remove(&p1)); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: failed to remove + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + // remove not found + person_t p2 = { "a", "b", 124 }; + CU_ASSERT_FALSE_FATAL(list_remove(&p2)); + // unchanged + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + + // act & assert: remove last + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + CU_ASSERT_TRUE_FATAL(list_remove(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + + // act & assert: remove first + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + CU_ASSERT_TRUE_FATAL(list_remove(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p2) == 0); + + // END-STUDENTS-TO-ADD-CODE +} + +static void test_list_clear(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + const node_t *anchor = list_init(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: empty list + list_clear(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: clear list of one + person_t p1 = { "a", "b", 123 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + list_clear(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: clear list of two + person_t p2 = { "a", "b", 124 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + list_clear(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // END-STUDENTS-TO-ADD-CODE +} + +/** + * @brief Registers and runs the tests. + * @returns success (0) or one of the CU_ErrorCode (>0) + */ +int main(void) +{ + // setup, run, teardown + TestMainBasic("lab test", setup, teardown + , test_person_compare + , test_list_insert + , test_list_remove + , test_list_clear + ); +} diff --git a/P07_Prozesse_und_Threads/Solution/A03.dot b/P07_Prozesse_und_Threads/Solution/A03.dot new file mode 100644 index 0000000..d56eab7 --- /dev/null +++ b/P07_Prozesse_und_Threads/Solution/A03.dot @@ -0,0 +1,76 @@ +digraph ProcessHierarchie { + + P1[label="P01\nfork()"]; + + P11[label="P01\nfork()"]; + P12[label="P02\nfork()"]; + + P111[label="P01\nfork()"]; + P112[label="P03\nfork()"]; + P121[label="P02\nfork()"]; + P122[label="P04\nfork()"]; + + P1111[label="P01\nfork()"]; + P1112[label="P05\nfork()"]; + P1121[label="P03\nfork()"]; + P1122[label="P06\nfork()"]; + P1211[label="P02\nfork()"]; + P1212[label="P07\nfork()"]; + P1221[label="P04\nfork()"]; + P1222[label="P08\nfork()"]; + + P11111[label="P01"]; + P11112[label="P09"]; + P11121[label="P05"]; + P11122[label="P10"]; + P11211[label="P03"]; + P11212[label="P11"]; + P11221[label="P06"]; + P11222[label="P12"]; + P12111[label="P02"]; + P12112[label="P13"]; + P12121[label="P07"]; + P12122[label="P14"]; + P12211[label="P04"]; + P12212[label="P15"]; + P12221[label="P08"]; + P12222[label="P16"]; + + P1 -> P11 + P1 -> P12 + + P11 -> P111 + P11 -> P112 + P12 -> P121 + P12 -> P122 + + P111 -> P1111 + P111 -> P1112 + P112 -> P1121 + P112 -> P1122 + P121 -> P1211 + P121 -> P1212 + P122 -> P1221 + P122 -> P1222 + + P1111 -> P11111 + P1111 -> P11112 + P1112 -> P11121 + P1112 -> P11122 + P1121 -> P11211 + P1121 -> P11212 + P1122 -> P11221 + P1122 -> P11222 + P1211 -> P12111 + P1211 -> P12112 + P1212 -> P12121 + P1212 -> P12122 + P1221 -> P12211 + P1221 -> P12212 + P1222 -> P12221 + P1222 -> P12222 + + { rank="same"; P11; P12; } +# { rank="same"; P1; P11; P111; P1111; P11111; } + +} diff --git a/P07_Prozesse_und_Threads/Solution/A03.png b/P07_Prozesse_und_Threads/Solution/A03.png new file mode 100644 index 0000000..0d95416 Binary files /dev/null and b/P07_Prozesse_und_Threads/Solution/A03.png differ diff --git a/P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.docx b/P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.docx new file mode 100644 index 0000000..7dac7ed Binary files /dev/null and b/P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.docx differ diff --git a/P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.pdf b/P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.pdf new file mode 100644 index 0000000..a138b82 Binary files /dev/null and b/P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.pdf differ diff --git a/P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.docx b/P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.docx new file mode 100644 index 0000000..6dcef23 Binary files /dev/null and b/P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.docx differ diff --git a/P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.pdf b/P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.pdf new file mode 100644 index 0000000..ed25ab5 Binary files /dev/null and b/P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.pdf differ diff --git a/P07_Prozesse_und_Threads/SolutionFromBSYDonn20210513/ProcThreadsLsg.pdf b/P07_Prozesse_und_Threads/SolutionFromBSYDonn20210513/ProcThreadsLsg.pdf new file mode 100644 index 0000000..ccc66ff Binary files /dev/null and b/P07_Prozesse_und_Threads/SolutionFromBSYDonn20210513/ProcThreadsLsg.pdf differ diff --git a/P08_Sync/SolutionFromBSYDonn20210513/SyncLsg.tar.gz b/P08_Sync/SolutionFromBSYDonn20210513/SyncLsg.tar.gz new file mode 100644 index 0000000..7486bb4 Binary files /dev/null and b/P08_Sync/SolutionFromBSYDonn20210513/SyncLsg.tar.gz differ diff --git a/P09_File_Operations/README.md b/P09_File_Operations/README.md new file mode 100644 index 0000000..8ecb004 --- /dev/null +++ b/P09_File_Operations/README.md @@ -0,0 +1,34 @@ +# 09 - File Operations + +___ + + +___ + +## 1. Übersicht + + +___ + +## 2. Lernziele + + + +___ + +## 3. Aufgabe 1: + +___ + +## 4. Bewertung +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| 1 | - | - | + + + +___ +Version: 16.02.2022 diff --git a/P10_IPC/README.md b/P10_IPC/README.md new file mode 100644 index 0000000..bacd4fa --- /dev/null +++ b/P10_IPC/README.md @@ -0,0 +1,31 @@ +# 10 - IPC + +___ + +## 1. Übersicht + + +___ + +## 2. Lernziele + + + +___ + +## 3. Aufgabe 1: + +___ + +## 4. Bewertung +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| 1 | - | - | + + + +___ +Version: 16.02.2022 diff --git a/README.md b/README.md index 8f28a8f..c9c7f84 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -# SNP - Laboratory Planning +# SNP - Praktika -### Description -TODO -### Views -[Github-Pages](https://github.zhaw.ch/pages/SNP/snp/) +### Übersicht +[Online Beschreibungen der Praktika und Aufgaben](https://github.zhaw.ch/pages/SNP/snp-lab-code/) -[.pdf](https://github.zhaw.ch/SNP/snp-lab-code/blob/master/main.pdf) +[Praktika.pdf](https://github.zhaw.ch/SNP/snp-lab-code/blob/master/build/latex/main.pdf) diff --git a/build/html/.buildinfo b/build/html/.buildinfo new file mode 100644 index 0000000..7660e1c --- /dev/null +++ b/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 5c323e642e12d9fbf3c26fc7fc5af7e8 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/build/html/.nojekyll b/build/html/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/build/html/P01_Erste_Schritte_mit_C/README.html b/build/html/P01_Erste_Schritte_mit_C/README.html new file mode 100644 index 0000000..3d5a704 --- /dev/null +++ b/build/html/P01_Erste_Schritte_mit_C/README.html @@ -0,0 +1,215 @@ + + + + + + + + + 01 - Erste Schritte mit C — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

01 - Erste Schritte mit C

+
+
+

1. Übersicht

+

In diesem Praktikum erstellen Sie mehrere kleine C-Programme, in denen Sie Input- und Output-Funktionen der C Standard Library verwenden.

+

Arbeiten Sie in Zweiergruppen und diskutieren Sie ihre Lösungsansätze miteinander, bevor Sie diese umsetzen.

+

Bevor Sie mit den Programmieraufgaben beginnen, setzen Sie eine virtuelle Maschine mit der vorbereiteten Praktikumsumgebung auf.

+
+
+
+

2. Lernziele

+

In diesem Praktikum schreiben Sie selbst von Grund auf einige einfache C-Programme und wenden verschiedene Kontrollstrukturen an.

+
    +
  • Sie können mit #include Funktionen der C Standard Library einbinden

  • +
  • Sie können mit #define Macros definieren und diese anwenden

  • +
  • Sie wenden die Input- und Output-Funktionen von C an, um Tastatur-Input einzulesen und formatierte Ausgaben zu machen.

  • +
  • Sie verwenden die Kommandozeile, um ihren Sourcecode in ein ausführbares Programm umzuwandeln.

  • +
  • Sie wenden for-und while-Loops sowie if-then-else-Verzweigungen an.

  • +
  • Sie setzen eine Programmieraufgabe selbständig in ein funktionierendes Programm um.

  • +
+
+
+
+

3. Aufgabe 1: virtuelle Maschine

+

Im Moodle-Kurs “Systemnahe Programmierung” finden Sie unter “Praktika” eine Installationsanleitung für die virtuelle Maschine, die wir Ihnen zur Verfügung stellen. Die virtuelle Maschine enthält ein Ubuntu Linux-Betriebssystem und die für das Praktikum benötigten Frameworks.

+

Folgen sie der Anleitung, um die virtuelle Maschine auf ihrem Rechner zu installieren.

+
+
+
+

4. Aufgabe 2: Hello World

+

Schreiben Sie ein C-Programm, das “Hello World” auf die Standardausgabe schreibt. Verwenden Sie die printf-Funktion aus der Standard Library. In den Vorlesungsfolien finden Sie bei Bedarf eine Vorlage.

+

Erstellen sie das Source-File mit einem beliebigen Editor, sie benötigen nicht unbedingt eine IDE. Speichern Sie das Source-File mit der Endung .c.

+

Um ihr Source-File zu kompilieren, verwenden Sie den GNU Compiler auf der Kommandozeile:

+
$> gcc hello.c
+
+
+

Der Compiler übersetzt ihr Programm in eine ausführbare Datei a.out, die Sie mit

+
$> ./a.out
+
+
+

ausführen können. Sie können den Namen der ausführbaren Datei wählen, indem Sie die Option -o verwenden:

+
$> gcc hello.c -o hello
+
+
+

erzeugt die ausführbare Datei hello.

+

Verwenden Sie die Option -Wall, um alle Warnungen des Compilers auszugeben. Dies weist Sie auf allfällige Programmierfehler hin.

+
+
+
+

5. Aufgabe 3: Tabellenausgabe

+

Schreiben Sie ein Programm in C, das von stdin einen Umrechnungsfaktor zwischen CHF und Bitcoin einliest und danach eine Tabelle von Franken- und Bitcoin-Beträgen ausgibt. Die Tabelle soll sauber formatiert sein, z.B. so:

+
Enter conversion rate (1.00 BTC -> CHF): 43158.47
+  200 CHF	<-->	 0.00463 BTC
+  400 CHF	<-->	 0.00927 BTC
+  600 CHF	<-->	 0.01390 BTC
+  800 CHF	<-->	 0.01854 BTC
+ 1000 CHF	<-->	 0.02317 BTC
+ 1200 CHF	<-->	 0.02780 BTC
+ 1400 CHF	<-->	 0.03244 BTC
+ 1600 CHF	<-->	 0.03707 BTC
+
+
+
    +
  • Verwenden Sie eine Schleife und die printf-Funktion für die Tabellenausgabe

  • +
  • Definieren Sie ein Makro NUM_ROWS, um an zentraler Stelle im Source-Code zu definieren, wie viele Einträge die Tabelle in der Ausgabe haben soll.

  • +
  • Lesen Sie den Umrechnungsfaktor mit der scanf-Funktion als double von der Kommandozeile ein.

  • +
+
+
+
+

6. Aufgabe 4: Zeichen und Wörter zählen

+

Schreiben Sie ein C-Programm, welches die Zeichen und Wörter einer mit der Tastatur eingegebenen Zeile zählt. Wortzwischenräume sind entweder Leerzeichen (’ ‘) oder Tabulatoren (‘\t’). Die Eingabe der Zeile mit einem newline-character (‘\n’) abgeschlossen. Danach soll ihr Programm die Anzahl Zeichen und die Anzahl Wörter ausgeben und terminieren.

+
    +
  • Verwenden Sie die char getchar(void) Funktion aus der stdio.h Library, um die Zeichen einzeln einzulesen. Die Funktion getchar kehrt nicht gleich bei Eingabe des ersten Zeichens zurück, sondern puffert die Daten, bis die Eingabe einer kompletten Zeile mit Return abgeschlossen wird. Dann wird das erste Zeichen aus dem Puffer zurückgegeben und mit weiteren Aufrufen von getchar können die nachfolgenden Zeichen aus dem Puffer gelesen werden. Gibt getchar das Zeichen \n zurück, ist die Zeile komplett zurückgegeben und der Puffer ist wieder leer.

  • +
  • Setzen Sie eine Schleife ein, die beim Zeichen ‘\n’ terminiert.

  • +
  • Benutzen Sie if-then-else-Strukturen um die Wörter zu zählen.

  • +
+
+
+
+

7. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P02_Funktionen_Datentyp_enum/README.html b/build/html/P02_Funktionen_Datentyp_enum/README.html new file mode 100644 index 0000000..17fca06 --- /dev/null +++ b/build/html/P02_Funktionen_Datentyp_enum/README.html @@ -0,0 +1,412 @@ + + + + + + + + + 02: Funktionen, Datentyp “enum” — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

02: Funktionen, Datentyp “enum”

+
+

+

(Copyright Bild: xkcd.com)

+
+
+

1. Übersicht

+

In diesem Praktikum sind zwei Themen im Fokus: Funktionen und der Datentyp enum.

+

Funktionen sind der wesentlichste Bestandteil der C Programmierung welcher eine strukturierte Programmierung ermöglicht:

+
    +
  • Eine Funktion ein Teil eines C Codes, der eine spezielle Aufgabe ausführt. Sie kann aus dem Hauptprogramm, oder aus anderen Funktionen, aufgerufen werden.

  • +
  • Jede Funktion besitzt einen eindeutigen Namen, eine eindeutige Signatur (Typen und Reihenfolge der Parameter) und einen Rückgabewert (int falls nichts angegeben wird).

  • +
  • Eine Funktion kann Werte aus dem aufrufendem Kontext übernehmen und bei Bedarf einen Wert an den aufrufenden Kontext zurückliefern. +Beispiel einer Additions-Funktion:

  • +
+
#include <stdio.h>
+
+/* Funktionsdeklaration */
+int add(int a, int b);
+
+int main(void) {
+   int aa = 1, bb = 2, cc;
+   printf("%aa + %bb = %cc", aa, bb, add(aa, bb););
+ return 0;
+}
+
+/* Funktionsdefinition */
+int add(int a, int b) {
+   return a + b;
+}
+
+
+

Der Daten typt enum wird verwendet um die Lesbarkeit von Programmen zu erhöhen:

+

Beispiel eines enum:

+
enum Ampeln = {rot =1, gelb, gruen};
+
+int main(void) {
+   Ampeln ampel1;
+   if (ampel1 == rot) {...} 
+ return 0;
+}
+
+
+
+
+
+

2. Lernziele

+

In diesem Praktikum lernen Sie Funktionen zu definieren und aufzurufen, sowie enum anzuwenden.

+
    +
  • Sie können ein Programm schreiben welches aus mehreren Funktionen besteht.

  • +
  • Sie können Funktionen deklarieren, definieren und aufrufen.

  • +
  • Sie können enum Typen definieren und deren Werte bestimmen und abfragen.

  • +
+
+
+
+

3. Aufgaben

+
+../_images/kalender-108_v-ARDFotogalerie.jpg +
+

(Copyright Bild: www.planet-wissen.de)

+
+

3.1 Aufgabe 1 Tage pro Monat

+

In der ersten Aufgabe berechnen Sie die Tag pro Monat einer beliebigen Kombination Monat / Jahr. +Erweitern Sie dazu das Programm um folgende Aspekte:

+
    +
  • Bereichsprüfung von Jahr und Monat

  • +
  • Funktion istSchaltjahr, welche berechnet, ob das Jahr eine Schaljahr ist

  • +
  • Funktion tageProMonat, welche die Anzahl Tage des gegebenen Monats und Jahres berechnet.

  • +
+

Vorgaben:

+
    +
  • Die Funktion istSchaltjahr nimmt ein Integer (jahr) entgegen und gibt 1 im Falle eiens Schltjahres und 0 im andreren Fall zurück

  • +
  • Die Funktion tageProMonat nimmt zwei integer (monat und jahr) entgegeben und gibt die Anzahl Tage als Integer zurück

  • +
  • Die Jahreszahl, welche den Funktionen übergeben wird, muss überprüft werden und grösser gleich 1599 und kleiner als 10000 sein

  • +
  • Der übergebene Monat muss grösser als 0 und kleine als 13 sein.

  • +
+

Die Regeln für die Schaltjahrberechnung:

+
    +
  • Schaltjahre sind alle Jahre, die durch 4 teilbar sind.

  • +
  • Eine Ausnahme bilden die Jahrhunderte (1600, 1700…). Diese sind keine Schltjahre.

  • +
  • zu den 100er gibt es ebenfalls Ausnahmen: Diese sind immer Schaltjahre, wenn sie durch 400 teilbar sind +… also zum Beispiel 1600 ist eines, nicht jedoch 1700. Weiterführende Details finden Sie unter https://de.wikipedia.org/wiki/Gregorianischer_Kalender

  • +
+

Gegeben ist die main Funktion des Programms. Ergänzen Sie die enum Definition und die fehlenden Funktionen:

+
    +
  • gibIntWert: Die Funktion soll einen Int Wert zurückgeben. Der Bereich, wie auch Fehleingaben sollen sollen berücksichtigt werden. (atoi unfd fgets sind hier hilfreich)

  • +
  • istSchaltjahr: Die Funktion gibt 1 im Falle eines Schltjahr und o im anderen Falle zurück.

  • +
  • tageProMonat: Die Funktion gibt den die Tage des Monats für das definierte Jahr zurück. Verwenden Sie die Switchanweisung , sowie den enum Datentypen

  • +
+
int main (int argc, char *argv[]) {
+    
+    int monat, jahr;
+     
+    //  Monat einlesen und Bereich ueberpruefen
+    monat = gibIntWert("Monat", 1, 12);
+    jahr  = gibIntWert("Jahr", 1600, 9999);
+	
+    //  Ausgabe zum Test
+    printf("Monat: %d, Jahr: %d \n", monat, jahr);    
+    
+    //  Ausgabe zum Test (hier mit dem ternaeren Operator "?:")
+    printf("%d ist %s Schaltjahr\n", jahr, istSchaltjahr(jahr) ? "ein" : "kein");
+
+	// Ausgabe
+	printf("Der Monat %02d-%d hat %d Tage.\n", monat, jahr, tageProMonat(jahr, monat));
+   
+    return 0;
+}
+
+
+

Tipp: Angenommen Sie verwenden den enum month_t { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ }; +Dann können Sie im Programm direkt die Konstanten verwenden:

+
if (m == 2) ...		// schlecht lesbar
+if (monat == 2) ...	// besserer Variablenname 
+if (monat == FEB) ...	// am besten lesbar
+
+
+

Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (make test)

+
+
+
+

3.2 Aufgabe 2 Bestimmen des Wochentags

+

Erweitern Sie das vorgegebene zweite Programm Gerüst an den bezeichneten Stellen so, dass das Programm von der Kommando Zeile ein Argument entgegennimmt, es auf Gültigkeit überprüft und schliesslich den Wochentag für das gegebene Datum berechnet und ausgibt. +Prüfen Sie die Umsetzung beider Teilaufgaben mittels make test.

+
+

3.2.1 Teilaufgabe Argumente Parsen und auf Korrektheit prüfen

+

Das Argument stellt ein gültiges Datum unseres Gregorianischen Kalenders dar (d.h. ein Datum ab Donnerstag, den 15. Oktober 1582, mit der Gregorianischen Schaltjahr Regel). +Wenn kein Argument gegeben ist oder wenn das eingegebene Datum nicht gültig ist, soll das Programm einem Hilfetext auf stderr ausgeben und mit EXIT_FAILURE Exit Code terminieren. Wenn ein gültiges Datum erkannt wurde terminiert das Programm mit Exit Code EXIT_SUCCESS.

+
+
3.2.1.1 Argument Format
+

Das Format des Kommando Zeilen Arguments soll yyyy-mm-dd sein, wobei yyyy für das vier-stellige Jahr, mm für einen 1-2-stelligen Monat (1…12) und dd für einen Tag des Monats, begin-nend mit 01. Z.B. 2020-02-29.

+
+
+
3.2.1.2 Korrektes Datum
+

Das Datum muss alle folgenden Bedingungen erfüllen damit es als korrekt erkannt wird:

+
    +
  • Obergrenze für ein «sinnvolles» Datum ist das Jahr 9999

  • +
  • es muss Gregorianisch sein, d.h. ab 15. Oktober 1582 (inklusive)

  • +
  • es darf nur Monate von 1 für Januar bis 12 für Dezember beinhalten

  • +
  • der Tag muss grösser oder gleich 1 sein

  • +
  • der Tag darf nicht grösser als 31 sein für Monate mit einer Länge von 31 Tagen

  • +
  • der Tag darf nicht grösser als 30 sein für Monate mit einer Länge von 30 Tagen

  • +
  • der Tag darf für den Februar nicht grösser sein als 29 für ein Schaltjahr

  • +
  • der Tag darf für den Februar nicht grösser sein als 28 für ein Nicht-Schaltjahr

  • +
+
+
+
3.2.1.3 Vorgaben an die Umsetzung
+
    +
  1. Definieren Sie einen enum Typen mit (typedef) Namen month_t dessen Werte die Englischen 3-Zeichen Abkürzungen der Monate sind, nämlich Jan, Feb, … Dec und stellen Sie sicher dass die Abkürzungen für die uns geläufigen Monatsnummer stehen.

  2. +
  3. Definierend Sie einen struct Typen mit (typedef) Namen date_t und den int Elementen year, month, day. Lesen Sie das Argument (falls vorhanden) via sscanf und dem Formatstring “%d-%d-%d” in die drei Elemente einer Date Variable. Siehe dazu die Hinweise im Anhang.

  4. +
  5. Für die Berechnung der Monatslänge implementieren Sie die Hilfsfunktion is_leap_year(date_t date) (nach obigen Vorgaben). Der Return Wert 0 bedeutet «Kein Schaltjahr», 1 bedeutet «Schaltjahr».

  6. +
  7. Implementieren Sie die Funktion int get_month_length(date_t date). Diese soll für den Monat des Datums die Monatslänge (was dem letzten Tag des Monats ent-spricht) ausgeben – geben Sie 0 für ungültige Monatswerte zurück.

  8. +
  9. Schliesslich implementieren Sie die Funktion int is_gregorian_date(date_t date) welche prüft, ob ein gegebenes Datum im Bereich 15. Oktober 1582 und dem Jahr 9999 ist (0 = nein, 1 = ja).

  10. +
  11. Implementieren Sie eine Funktion int is_valid_date(date_t date), welche obige Bedingungen für ein gültiges Datum umsetzt. Der Return Wert 0 bedeutet «Kein gültiges Datum», 1 bedeutet «Gültiges Datum». Benutzen Sie für die Prüfung des Datums die month_t Werte wo immer möglich und sinnvoll. Verwenden Sie die oben implemen-tierten Hilfsfunktionen.

  12. +
+
+
+
3.2.1.4 Hinweise
+

Beachten Sie die Kommentare im Code für die geforderten Implementierungs-Details.

+
+
+
+

3.2.2 Teilaufgabe Wochentag Berechnung

+

Schreiben Sie eine Funktion welche zu einem Datum den Wochentag berechnet. +Die Formel wird Georg Glaeser zugeschrieben, möglicherweise angelehnt an eine Formel von Carl Friedrich Gauss.

+
+../_images/Wochentagsberechnung.jpg +
+

(Quelle: https://de.wikipedia.org/wiki/Wochentagsberechnung)

+

Hier ist eine für C abgewandelte Variante davon.

+
weekday = ((day + (13 * m - 1) / 5 + y + y / 4 + c / 4 - 2 * c) % 7 + 7) % 7
+alle Zahlen sind int Werte und alles basiert auf int-Arithmetik
+m = 1 + (month + 9) % 12
+a = year - 1 (für month < Mar), ansonsten year 
+y = a % 100
+c = a / 100
+
+
+

Erweitern sie das Programm so, dass vor dem erfolgreichen Terminieren des Programms fol-gende Zeile (inklusive Zeilenumbruch) ausgegeben wird: yyyy-mm-dd is a Ddd, wobei yyyy für das Jahr, mm für die Nummer des Monats (01…12) und dd für den Tag im Monat (01…). Z.B. 2020-02-29 is a Sat. +Vorgaben an die Umsetzung

+
    +
  1. Definieren Sie einen enum Typen mit (typedef) Namen weekday_t dessen Werte die Englischen 3-Zeichen Abkürzungen der Tage sind, nämlich Sun, Mon, … Sat und stel-len Sie sicher dass die Abkürzungen für die Werte 0…6 stehen.

  2. +
  3. Schreiben Sie eine Funktion weekday_t calculate_weekday(date_t date) nach der Beschreibung der obigen Formel. Das date Argument ist als gültig angenom-men, d.h. es ist ein Programmier-Fehler, wenn das Programm diese Funktion mit einem ungültigen Datum aufruft. Machen Sie dafür als erste Codezeile in der Funktion eine Zu-sicherung (assert(is_valid_date(date));)

  4. +
  5. Schreiben Sie eine Funktion void print_weekday(weekday_t day), welche für jeden gülteigen Tag eine Zeile auf stdout schreibt mit den Englischen 3-Zeichen Ab-kürzungen für den Wochentag, z.B. Sonntag: Sun, Montag: Mon, etc. Wenn ein ungülti-ger Wert für day erkannt wird, soll assert(!”day is out-of-range”); aufgeru-fen werden. +Hinweise +• Für interessierte, siehe: https://de.wikipedia.org/wiki/Wochentagsberechnung

  6. +
+
+
+
+
+
+

4. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden können.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Gewicht

alle

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

gibIntWert

Eingabe, Bereichsüberprüfung korrekt

1

istSchaltjahr

Funktion korrekt

1

TageProMonat

Funktion korrekt

1

Aufgabe 2

Fehlenden Teile ergänzt und lauffähig

1

+
+
+
+

5. Anhang

+
+

5.1 Sprach Element

+
  ...
+}	argc: Anzahl Einträge in argv.
+argv: Array von Command Line Argumenten.
+argv[0]: wie das Programm gestartet wurde
+argv[1]: erstes Argument
+…
+argv[argc-1]: letztes Argument
+int a = 0;
+int b = 0;
+int c = 0;
+int res = sscanf(argv[1]
+                , "%d-%d-%d"
+                , &a, &b, &c
+                );
+if (res != 3) {
+    // Fehler Behandlung...
+    // ...
+}	
+
+
+
+
+

5.2 Beschreibung

+

Siehe man 3 sscanf. +Die Funktion sscanf gibt die Anzahl erfolgreich erkannte Argumente zurück. Unbedingt prüfen und angemessen darauf reagieren. +Die gelesenen Werte werden in a, b und c, gespeichert, dazu müssen Sie die Adresse der Variablen übergeben. Mehr Details dazu werden später erklärt. +fprintf(stderr, “Usage: %s…\n”, argv[0]); Siehe man 3 fprintf. +Schreibt formatierten Text auf den stderr Stream.

+
+

Version: 15.02.2022

+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P02_Funktionen_Datentyp_enum/README_solution.html b/build/html/P02_Funktionen_Datentyp_enum/README_solution.html new file mode 100644 index 0000000..7ee05b4 --- /dev/null +++ b/build/html/P02_Funktionen_Datentyp_enum/README_solution.html @@ -0,0 +1,245 @@ + + + + + + + + + Lösungsskizzen — SNP Labs documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Lösungsskizzen

+
+

Aufgabe 1

+
/**
+ *  Tage Pro Monat
+ *
+ *  Das Programm liest einen Monat (1-12) und ein Jahr (1600-2400) ein und
+ *  gibt die Anzahl der Tage dieses Monats aus.
+ *
+ *  @author Gerrit Burkert, Adaptation bazz
+ *  @version 15-FEB-2013, 16-OCT-2017, 17-OCT-2019, 16-FEB-2022
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ERROR_IN_MONTH 1
+#define ERROR_IN_YEAR 2
+
+///// Student Code
+
+
+
+// Konstante Werte fuer die Monate
+// ===============================
+
+enum { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ };
+
+
+// Eingabe pruefen (0 ist vom atoi als Fehelcode verwendet und darf nicht verwendet werden) 
+// ===============
+
+int gibIntWert(char *name, int von, int bis) {
+	
+	int wert;
+	char wertS[20]; //
+	
+	do {
+    	printf("%s:     ", name);
+    	fgets(wertS, 20, stdin);
+    	wert = atoi(wertS);
+    	if (wert < von || wert > bis) {
+        	printf("Der Wert muss zwischen %d und %d sein.\n", von, bis);
+    	} else {
+    		break; 
+		}
+	} while(1);
+	return wert;
+}
+
+
+// Schaltjahr bestimmen
+// ==================== 
+
+int istSchaltjahr(int jahr){
+	
+	if ( (jahr % 400 == 0) || ( (jahr %100 != 0) && (jahr % 4 ==0) ) )
+		return 1;
+	else
+		return 0;
+}
+
+
+// Berechnung Anzahl Tage pro Monat
+// ================================
+
+int tageProMonat(int jahr, int monat) {
+
+	int anzTage;
+    
+    // Tage pro Monat bestimmen
+    switch (monat) {
+	   
+        // Monate mit 31 Tagen 
+        case JAN: case MAR: case MAI: case JUL: case AUG: case OKT: case DEZ:
+            anzTage = 31;
+            break;
+        
+        // Monate mit 30 Tagen 
+        case APR: case JUN: case SEP: case NOV:
+            anzTage = 30;
+            break;
+        
+        // Februar: 28 oder 29 Tage 
+        case FEB:
+		
+            if (istSchaltjahr(jahr)) {
+                anzTage = 29;
+            } else {
+                anzTage = 28;
+            }
+            break;
+    }
+
+	return anzTage;
+}
+
+///// END Student Code
+
+
+int main (int argc, char *argv[]) {
+    
+    int monat, jahr;
+     
+    //  Monat einlesen und Bereich ueberpruefen
+    monat = gibIntWert("Monat", 1, 12);
+    jahr  = gibIntWert("Jahr", 1600, 9999);
+	
+    //  Ausgabe zum Test
+    printf("Monat: %d, Jahr: %d \n", monat, jahr);    
+    
+    //  Ausgabe zum Test (hier mit dem ternaeren Operator "?:")
+    printf("%d ist %s Schaltjahr\n", jahr, istSchaltjahr(jahr) ? "ein" : "kein");
+
+	// Ausgabe
+	printf("Der Monat %02d-%d hat %d Tage.\n", monat, jahr, tageProMonat(jahr, monat));
+   
+    return 0;
+}
+
+
+
+
+

Aufgabe 2

+

Alter bestehender Boilerplate Code

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P03_Bit_Operation_struct_typedef/README.html b/build/html/P03_Bit_Operation_struct_typedef/README.html new file mode 100644 index 0000000..cb958f4 --- /dev/null +++ b/build/html/P03_Bit_Operation_struct_typedef/README.html @@ -0,0 +1,372 @@ + + + + + + + + + 03 - Bit Operationen, Struct, Typedef — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

03 - Bit Operationen, Struct, Typedef

+
+

1. Bit Operationen

+

+

Bit Operationen sind allgegenwärtig in den Computer-Wissenschaften und finden in vielen Disziplinen Anwendung. Folgend ein kleiner Auszug aus den wichtigsten Themen:

+
    +
  • Bit Felder: Sind die effizienteste Art, etwas darzustellen, dessen Zustand durch mehrere “wahr” oder “falsch” definiert werden kann. Besonders auf Systemen mit begrenzten Ressourcen sollte jede überflüssige Speicher-Allozierung vermieden werden.

    +

    Beispiel:

    +
    // primary colors
    +#define BLUE  0b100
    +#define GREEN 0b010
    +#define RED   0b001
    +
    +// mixed colors
    +#define BLACK   0 /* 000 */
    +#define YELLOW  (RED | GREEN) /* 011 */
    +#define MAGENTA (RED | BLUE) /* 101 */
    +#define CYAN    (GREEN | BLUE) /* 110 */
    +#define WHITE   (RED | GREEN | BLUE) /* 111 */
    +
    +
    +
  • +
+

https://de.wikipedia.org/wiki/Bitfeld

+
    +
  • Kommunikation:

    + +
  • +
  • Datenkompression: Bei der Datenkompression wird versucht, redundante Informationen zu entfernen. Dazu werden die Daten in eine Darstellung überführt, mit der sich alle – oder zumindest die meisten – Information in kürzerer Form darstellen lassen. +https://de.wikipedia.org/wiki/Datenkompression

  • +
  • Kryptographie: Konzeption, Definition und Konstruktion von Informationssystemen, die widerstandsfähig gegen Manipulation und unbefugtes Lesen sind. https://de.wikipedia.org/wiki/Verschl%C3%BCsselung

  • +
  • Grafik-Programmierung: XOR (oder ^) ist hier besonders interessant, weil eine zweite Eingabe derselben Eingabe die erste rückgängig macht (ein Beispiel dazu weiter unten: “Variablen tauschen, ohne Dritt-Variable +“). Ältere GUIs verwendeten dies für die Hervorhebung von Auswahlen und andere Überlagerungen, um kostspielige Neuzeichnungen zu vermeiden. Sie sind immer noch nützlich in langsamen Grafikprotokollen (z. B. Remote-Desktop).

  • +
+
+

1.1 Übungen

+
+

1. Basis Operationen

+

Manipulationen von einzelnen Bits gehören zu den Basis Operationen und dienen als Grundlagen um weitere komplexere Konstrukte zu schaffen. Verfollständigen sie folgendes Beispiel mit den drei Basis Operationen:

+
#include <stdlib.h>
+
+int main() {
+  unsigned int number;
+  unsigned int bit = 3; // bit at position 3
+
+  // Setting a bit
+  number = ...; // solution: number |= 1 << bit;
+  
+  // Clearing a bit
+  number = ...; // solution: number &= ~(1 << bit);
+  
+  // Toggling a bit
+  number = ...; // solution; number ^= 1 << bit;
+  
+  return EXIT_SUCCESS;
+}
+
+
+
+
+

2. Variablen tauschen (ohne Dritt-Variable)

+

Zwei Variablen zu vertauschen scheint ein einfach lösbares Problem zu sein. Eine offensichtliche Variante wäre mittels einer temporären Variablen:

+
#include <stdlib.h>
+#include <stdio.h>
+
+int main(){
+  int a = 3;
+  int b = 4;
+  printf("a: %d; b: %d\n", a, b);
+  
+  int temp = a;
+  a = b;
+  b = temp;
+  
+  printf("a: %d; b: %d\n", a, b);
+  return EXIT_SUCCESS;
+}
+
+
+

Es gibt aber auch eine Variante, die ohne zusätzliche Variable auskommt. Dabei wird die Tatsache, dass eine zweite XOR Operation eine erste XOR Operation rückgängig macht:

+

0011 XOR 0100 = 0111

+

0111 XOR 0100 = 0011

+

Somit kommt man von einem XOR Resultat (0111) wieder auf beide Anfangs Operanden zurück indem man einfach ein zweites Mal mit einem Operanden eine XOR Verknüpfung macht. Damit kann ein Operand als Zwischenspeicher dienen und man muss nicht extra eine Zusatzvariable verwenden.

+

Überlegen sie sich wie sie damit zwei Variablen vertauschen können ohne Zusatzvariable:

+
#include <stdlib.h>
+#include <stdio.h>
+
+int main(){
+  int a = 3;
+  int b = 4;
+  printf("a: %d; b: %d\n", a, b);
+  
+
+  ...
+
+  /* Solutions: 
+          // a == 0011; b == 0100
+  a ^= b; // a == 0111; b == 0100
+  b ^= a; // a == 0111; b == 0011
+  a ^= b; // a == 0100; b == 0011
+  */
+  
+  printf("a: %d; b: %d\n", a, b);
+  return EXIT_SUCCESS;
+}
+
+
+
+
+

3. Lower- / Uppercase

+
#include <stdlib.h>
+#include <stdio.h>
+
+int main(){
+  char word[8] = "sREedEv";
+  char *wordptr = &word[0];
+
+  while(wordptr < &word[7]) {
+    printf("UPPERCASE: %c\n", *wordptr & '_'); // converts the char into uppercase regardless of the current casing
+    printf("LOWERCASE: %c\n", *wordptr | ' '); // converts the char into lowercase regardless of the current casing
+    wordptr++;
+  }
+
+  return EXIT_SUCCESS;
+}
+
+
+
+
+

4. Prüfen auf 2-er Potenz

+
#include <stdio.h>
+#include <stdlib.h>
+
+int main(){
+  int a=32;
+  if(a > 0 && (a & (a - 1)) == 0){
+    printf("%d is a power of 2", a);
+  }
+  return EXIT_SUCCESS;
+}
+
+
+
+
+
+
+
+

2. Struct & typedef

+
+

2.1 Übungen

+
+

1. Bit Operationen Rechner

+
    +
  • Bitweise Operationen mit 2 Operanden

  • +
  • Rechnung wird als ein String über scanf dem Programm übergeben

    +
      +
    • String wird in Token zerstückelt und in struct gespeichert:

      +
      typedef struct {
      +  unsigned int operand_1;
      +  unsigned int operand_2;
      +  char operation;
      +} Expression;
      +
      +
      +
    • +
    • Ausgabe in 3 verschiedenen Formaten:

      +
      Bin:
      +  0000'0000'0000'0001
      +& 0000'0000'0000'0011
      +  -------------------
      +  0000'0000'0000'0001
      +  
      +  Hex
      +  0x01 & 0x03 = 0x01
      +
      +  Dec
      +  1 & 3 = 1
      +
      +
      +
    • +
    +
  • +
+
+
+
+
+
+

4. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden können.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Gewicht

alle

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

gibIntWert

Eingabe, Bereichsüberprüfung korrekt

1

istSchaltjahr

Funktion korrekt

1

TageProMonat

Funktion korrekt

1

Aufgabe 2

Fehlenden Teile ergänzt und lauffähig

1

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P04_Modularisieren_von_C_Code/README.html b/build/html/P04_Modularisieren_von_C_Code/README.html new file mode 100644 index 0000000..c9a2f44 --- /dev/null +++ b/build/html/P04_Modularisieren_von_C_Code/README.html @@ -0,0 +1,542 @@ + + + + + + + + + 04 - Modularisieren von C Code — SNP Labs documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

04 - Modularisieren von C Code

+ +
+ +
+
+

Inhalt

+

1. Übersicht

+

2. Lernziele

+

3. Aufgabe 1: Modularisieren

+

4. Aufgabe 2: Makefile Regeln

+

5. Bewertung

+

6. Anhang

+
+
+
+

1. Übersicht

+

In diesem Praktikum üben Sie modulare Programmierung indem Sie ein +Java Programm (bestehend aus drei Java Files) in ein entsprechendes C +Programm aus drei Modulen (aus je einem Header- und Implementations- +File) übersetzen. Sie passen das Makefile so an, dass die +entsprechenden Module mit kompiliert werden.

+

In der zweiten Aufgabe erstellen Sie Makefile Regeln für die drei +Schritte von den C Source Files zur graphischen Darstellung der +Abhängigkeiten.

+
+../_images/uebersicht.png +
+

Im Anhang ist eine Übersicht über die verwendeten File Formate gegeben.

+
+
+

2. Lernziele

+

In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen.

+
    +
  • Sie wissen, dass ein Modul aus einem C-File und einem passenden +H-File besteht.

  • +
  • Sie können Header Files korrekt strukturieren.

  • +
  • Sie deklarieren im Header-File die öffentlichen Typen und Funktionen +eines Moduls.

  • +
  • Sie wissen wie Include Guards anzuwenden sind.

  • +
  • Sie können Module im Makefile zur Kompilation hinzufügen.

  • +
  • Sie können Makefile Regeln schreiben.

  • +
+

Die Bewertung dieses Praktikums ist am Ende angegeben.

+

Erweitern Sie die vorgegebenen Code Gerüste, welche im git +Repository snp-lab-code verfügbar sind.

+
+
+

3. Aufgabe 1: Modularisieren

+

Das zu ergänzende Programm dep2dot hat folgende Funktionalität:

+

Ergänzen Sie in modularize/src den Code in triangle.c, +read.h, read.c, rectang.h und rectang.c so +dass die Tests erfolgreich durchlaufen. Die C Implementation soll +dieselbe Funktionalität haben wie die gegebenen Java Files. Lehnen Sie +sich so nahe wie möglich an die Java Files an.

+
    +
  1. In den Header-Files implementieren Sie den Include-Guard und +deklarieren Sie die öffentlichen Funktionen und gegebenenfalls +#define.

  2. +
  3. In den Implementations-Files implementieren Sie die Funktionen.

  4. +
+

Die drei Java Files liegen in modularize/java.

+
+

Tipps

+
    +
  • Implementieren Sie die Symbole welche vollständig in Grossbuchstaben +geschrieben sind als #define.

  • +
  • EOF kommt schon aus stdio.h und sollte deshalb nicht +mehr definiert werden.

  • +
  • Jene #define welche von andern Modulen verwendet werden +kommen ins Header-File, die andern ins Implementations-File.

  • +
  • Ein Grossteil des Java Codes aus den Methoden Bodies kann +eins-zu-eins in C übernommen werden. Listen Sie auf welche +Unterschiede es gibt:

  • +
+ + + + + + + + + + + + + + + + + +
JavaC
+
byte
+
+
+
+
boolean
+
+
+
+
true
+
+
+
+
false
+
+
+
+
System.out.print(…)
+
+
+
+
System.out.println(…)
+
+
+
+
System.in.read()
+
+
+
+
byte[] buffer = new byte[BUFFERSIZE];
+
+
+
+
public class rectang {
+    public boolean Rectangular(…)
+    { … }
+}
+
+
+
+
public class read {
+    public int getInt(...)
+        throws java.io.IOException
+    { ... }
+}
+
+
+
+
class triangle {
+    public static void main(String[] args)
+        throws java.io.IOException
+    { ... }
+}
+
+
+
+
read ReadInt = new read();
+...
+word = ReadInt.getInt(MAX_NUMBER);
+
+
+
+
rectang Rect = new rectang();
+...
+if (Rect.Rectangular(a, b, c) == true) { ... }
+
+
+
+
System.out.println(
+"-> Dreieck " + a + "-" + b + "-" + c
++ " ist rechtwinklig");
+
+
+
+
+
+
+

4. Aufgabe 2: Makefile Regeln

+

Die folgenden drei Schritte erstellen von einem C Source File eine +graphische Darstellung der Abhängigkeiten:

+
    +
  1. gcc ... -H .. file.c ... 2> file.dep (Regeln im Makefile bereits vorhanden)

  2. +
  3. dep2dot file.c <file.dep >file.dot (in dieser Aufgabe zu erstellen)

  4. +
  5. dot -Tpng file.dot >file.png (in dieser Aufgabe zu erstellen)

  6. +
+

Sie sollen für die Compiler-ähnlichen Programme dep2dot und dot +Makefile Regeln schreiben.

+
+../_images/uebersicht.png +
+

Das Programm dep2dot hat folgende Funktionalität:

+
    +
  1. Es liest von stdin die vom Compiler generierten +Abhängigkeits-Daten in Form des dep Formates ein.

  2. +
  3. Das erste und einzige Command Line Argument gibt das File an für +welches die von stdin gelesenen Abhängigkeiten gelten.

  4. +
  5. Auf stdout werden die Abhängigkeiten von stdin übersetzt als +dot-File Format ausgegeben.

  6. +
+

Das Programm dot hat folgende Funktionalität:

+
    +
  1. Es liest die textuelle Beschreibung eines Graphen aus der +übergebenen Datei (erstes Argument) ein.

  2. +
  3. Auf stdout wird die grafische Darstellung der Beschreibung der +Eingabe-Datei im png-File Format ausgegeben.

  4. +
+

Das dep-Format und das dot-Format sind im Anhang beschrieben.

+

Sie können die Funktionalität des Programms dep2dot kennen lernen, +indem Sie folgende Zeilen auf der Bash Shell ausführen. Das +dep.input File ist Teil der automatisierten Test Suite im +Verzeichnis tests:

+
bin/dep2dot dir/file <tests/dep.input >dep.dot
+dot -Tpng dep.dot >dep.png
+firefox dep.png
+
+
+

Als Resultat sollte Firefox folgende Graphik darstellen:

+
+../_images/dep_dot.png +
+

Definieren Sie im Makefile Regeln, welche die einzelnen Schritte von +den Source Files zu den png Files ausführen.

+

Prüfen Sie schliesslich die Umsetzung Aufgabe mittels make dep-clean dep && firefox src/*.png.

+
+

4.1 Neue Regeln hinzufügen

+

Führen Sie im Makefile an den angegebenen Stellen folgende +Ergänzungen durch

+
    +
  • definieren Sie eine Variable DEPFILES deren Inhalt die Liste alle +Einträge der Variable SOURCES ist, wobei bei allen die Endung .c +durch die Endung .c.png ersetzt ist

  • +
  • fügen Sie zum Pseudo-Target .PHONEY das Target dep dazu – dies +besagt, dass das später folgenden Target dep nicht ein File +repräsentiert (ohne dieses Setting würde make gegebenenfalls nach +einem File mit Namen dep suchen um zu entscheiden ob es +inkrementell gebildet werden muss)

  • +
  • schreiben Sie das Target dep gemäss der Beschreibung im Makefile

  • +
  • schreiben Sie die Suffix Regel für die Übersetzung von .png <- .dot gemäss Vorgabe im Makefile (als Inspiration, siehe auch die +%.c.dep: %.c Suffix Regel weiter unten im Makefile) – erklären +Sie was die Regel macht

  • +
  • schreiben Sie die Suffix Regel für die Übersetzung von .dot <- .dep gemäss Vorgabe im Makefile – erklären Sie was die Regel +macht

  • +
+

Die Umsetzung der obigen Änderungen sind erfolgreich, wenn Sie +folgende Shell Command Line erfolgreich ausführen können und in +Firefox die Abhängigkeiten der C-Files von den Inclu-de Files +dargestellt wird.

+

make dep-clean dep && firefox src/*.png.

+
+
+
+

5. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

1

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

1

Module einbinden, Header Files schreiben

2

2

Sie können das funktionierende Makefile demonstrieren und erklären.

2

Neue Regeln hinzufügen

2

+
+
+

6. Anhang

+
+

6.1 Verwendete zusätzliche Sprach Elemente

+ + +
+

Sprach Element

+
+

Beschreibung

+
+
fprintf(stderr, "v=%d", v)
+
+
+
+

Formatierte Ausgabe auf den Standard Error Stream. Siehe man 3 +stderr und man 3 fprintf.

+
+
+
+

6.2 Verarbeitung und verwendete File Formate

+

Das Programm in diesem Praktikum ist Teil für die graphische +Darstellung von #include File Abhängigkeit von C Files.

+

Den ersten Schritt für die Darstellung der #include File +Abhängigkeiten bietet der Compiler. Der Compiler kann mittels der -H +Command Line Option auf stderr ein Text File generieren, welches die +tatsächlich verwendeten Header Files auflistet. Zusätzlich wird im +Resultat die Verschachtelungstiefe der Includes angegeben.

+

Im zweiten Schritt übersetzt das Programm (dep2dot) dieses +Praktikums solche Dependency Files (dep) in eine Text Repräsentation +der Abhängigkeiten (dot) welche in graphische Darstel-lung (png) +übersetzt werden kann.

+

Als Tool zur Übersetzung der dot Files in das png Format dient das +dot Tool. Dieses Tool muss gegebenenfalls installiert werden:

+

sudo apt install graphviz

+

Die png Files können dann z.B. in der Programm Dokumentation +integriert werden (Darstellung zu Test Zwecken z.B. mittels firefox file.png).

+
+

6.2.1 dep File

+

Siehe: man gcc

+
-H  Print the name of each header file used, in addition to other
+    normal activities.  Each name is indented to show how deep in the
+    #include stack it is. [...]
+
+
+

Das File wird auf stderr ausgegeben.

+

Beispiel File (für Abhängigkeiten des main.c Files des dep2dot Programms)

+
. /usr/include/stdio.h
+.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
+... /usr/include/features.h
+.... /usr/include/x86_64-linux-gnu/sys/cdefs.h
+..... /usr/include/x86_64-linux-gnu/bits/wordsize.h
+..... /usr/include/x86_64-linux-gnu/bits/long-double.h
+.... /usr/include/x86_64-linux-gnu/gnu/stubs.h
+..... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
+.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
+.. /usr/include/x86_64-linux-gnu/bits/types.h
+... /usr/include/x86_64-linux-gnu/bits/wordsize.h
+... /usr/include/x86_64-linux-gnu/bits/typesizes.h
+.. /usr/include/x86_64-linux-gnu/bits/types/__FILE.h
+.. /usr/include/x86_64-linux-gnu/bits/types/FILE.h
+.. /usr/include/x86_64-linux-gnu/bits/libio.h
+... /usr/include/x86_64-linux-gnu/bits/_G_config.h
+.... /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
+.... /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
+... /usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h
+.. /usr/include/x86_64-linux-gnu/bits/stdio_lim.h
+.. /usr/include/x86_64-linux-gnu/bits/sys_errlist.h
+. /usr/include/stdlib.h
+.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
+.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
+.. /usr/include/x86_64-linux-gnu/bits/floatn.h
+... /usr/include/x86_64-linux-gnu/bits/floatn-common.h
+.... /usr/include/x86_64-linux-gnu/bits/long-double.h
+.. /usr/include/x86_64-linux-gnu/bits/stdlib-float.h
+. src/error.h
+.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
+. src/data.h
+.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
+. src/output.h
+Multiple include guards may be useful for:
+/usr/include/x86_64-linux-gnu/bits/stdlib-float.h
+/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
+/usr/include/x86_64-linux-gnu/bits/typesizes.h
+/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
+/usr/include/x86_64-linux-gnu/gnu/stubs.h
+
+
+
+
+

6.2.2 dot File

+

Graphviz ist ein mächtiges Tool-Set welches Graphen, definiert in +einem dot-Text File, automatisch anordnet und in png, gif und +andere Formate übersetzt.

+

Siehe die offizielle Web-Page +https://www.graphviz.org/.

+

Es gibt als Teil dieses Tool-Sets verschiedene Übersetzer. Der hier +verwendete ist der Basis-übersetzer: dot.

+

Das dot-File Format kennt viele Möglichkeiten die Knoten und Kanten +eines Graphen und de-ren Anordnung anzugeben.

+

Der Vorteil eines solchen Tool-Sets ist, dass man den Inhalt (den +Graphen) einfach definieren kann und sich nicht um das komplexe +Problem der ansprechenden Visualisierung kümmern muss.

+

Beispiel File (dot -Tpng sample.dot > sample.png)

+
digraph G {
+   node [shape=box]
+   A [label="a.c"];
+   B [label="a.h"];
+   C [label="b.h"];
+
+   subgraph cluster_c0 {
+      label="main"; color=black;
+      A;
+   }
+
+   subgraph cluster_c1 {
+      label="others"; style=filled; col-or=lightgrey;
+      { B; C; rank=same; }
+   }
+
+   A -> B;
+   A -> C;
+   B -> C;
+}
+
+
+
+
+

6.2.3 png File

+

Das png Format ist ein verlustfrei komprimiertes Raster Graphik +Format. Es wird oft in Web Pages verwendet.

+
+

Version: 15.02.2022

+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.html b/build/html/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.html new file mode 100644 index 0000000..a35b2f0 --- /dev/null +++ b/build/html/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.html @@ -0,0 +1,289 @@ + + + + + + + + + 04 - Modularisieren von C Code — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

04 - Modularisieren von C Code

+
+
+

1. Übersicht

+

In diesem Praktikum wird eine kleine Sammlung von Funktionen als Modul erstellt.

+

In der ersten Aufgabe schreiben Sie zu einem bestehenden C Programm die notwendigen Header Files plus passen das Makefile so an, dass die entsprechenden Module mit kompiliert werden.

+

In der zweiten Aufgabe erstellen Sie Makefile Regeln um aus Konfigurationsdateien graphischen Darstellungen zu erzeugen.

+
+
+
+

2. Lernziele

+

In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen.

+
    +
  • Sie wissen, dass ein Modul aus einem C-File und einem passenden H-File bestehen.

  • +
  • Sie können Header Files korrekt strukturieren.

  • +
  • Sie wissen wie Include Guards anzuwenden sind.

  • +
  • Sie können Module im Makefile zur Kompilation hinzufügen.

  • +
  • Sie können anhand einer Beschreibung Typen und Funktionen in den passenden Header Files deklarieren.

  • +
  • Sie können Makefile Regeln schreiben.

  • +
+

Die Bewertung dieses Praktikums ist am Ende angegeben.

+

Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind.

+
+
+
+

3. Aufgabe 1: Modularisieren

+

+
+

3.1 Teilaufgabe Modules einbinden, Header Files schreiben

+
    +
  • src/objects.h

    +
      +
    • 2 Datenstukturen definieren

    • +
    • struct point mit 2 double für x und y Koordinate

    • +
    • struct line mit 2 point

    • +
    +
  • +
  • src/functions.h und .c

    +
      +
    • 2 Funktionen deklarieren und definieren

    • +
    • Berechnung der Länge get_lengtheiner Linie (Annahme: Koordinaten sind alle positiv)

      +
        +
      • l = sqrt(h^ 2 + b^ 2)

      • +
      • ev. muss hier in den Anhang #include <math.h>

      • +
      +
    • +
    • Berechnung der Steigung get_slope der Linie gegenüber dem Koordinatensystem

      +
        +
      • m = h / b

      • +
      +
    • +
    +
  • +
  • tests vorgeben

  • +
  • src/objects.h

    +
      +
    • Include Guard

    • +
    • Includes

    • +
    • Struct für Punkt und Linie

    • +
    • Include Guard

    • +
    +
  • +
  • src/functions.h

    +
      +
    • Include Guard

    • +
    • Includes

    • +
    • Deklarationen der Funktionen für Berechnung der Länge und Steigung

    • +
    • Include Guard

    • +
    +
  • +
  • src/functions.c

    +
      +
    • Includes

    • +
    • Definitionen der Funktionen für Berechnung der Länge und Steigung

    • +
    • Include Guard

    • +
    +
  • +
+
+
+
+
+

4. Aufgabe 2: Makefile Regeln

+

Makefile ergänzen, damit Modul functions korrekt eingebunden und kompiliert wird.

+
    +
  1. Kompilieren Sie das ganze mittels make clean default. Es sollten keine Compiler Fehler auftreten.

  2. +
+
+

4.1 Neue Regeln hinzufügen

+
    +
  • Vorraussetzung: tab2svg.sh aus Praktikum 3 wird um die Möglichkeit erweitert eine Linie zu zeichnen (line:x1:y1:x2:y2:color)

  • +
  • Studierende erstellen

    +
      +
    • mind. 2 Files long.line und short.line mit 2 unterschiedlichen Linien

    • +
    • Makefile Regeln um aus einem File .line ein File .svg mit Hilfe des Scripts zu erstellen

    • +
    • PHONY Regel display um beide .svg mit Firefox darzustellen

      +
        +
      • Vorgabe: sie sollen eine Variable für die Input-Dateien nutzen

      • +
      +
    • +
    +
  • +
+

Nachdem das Programm in Aufgabe 1 umgesetzt ist, geht es nun darum, im Makefile Regeln zu definieren welche die einzelnen Schritte von den Source Files zu den png Files ausführen.

+

Prüfen Sie schliesslich die Umsetzung mittels make display.

+
+
+
+
+

5. Aufgabe 3

+
    +
  • Studierende sollen Ausgabe von make doc analysieren und die Include Diagramme erklären können

  • +
+
make doc
+firefox doc/index.html &
+
+
+
+
+
+

6. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+
+
+
+

7. Erweiterung Doxyfile für Abhängigkeitsanalyse

+
--- /home/vagrant/huno/snp-new/snp/praktika/Shared/work/Doxyfile	2022-02-07 21:16:42.343302707 +0100
++++ /home/vagrant/snp/Doxyfile	2022-02-07 22:22:36.266839126 +0100
+@@ -297,14 +297,14 @@
+ UML_LOOK               = NO
+ UML_LIMIT_NUM_FIELDS   = 10
+ TEMPLATE_RELATIONS     = NO
+-INCLUDE_GRAPH          = NO
+-INCLUDED_BY_GRAPH      = NO
++INCLUDE_GRAPH          = YES
++INCLUDED_BY_GRAPH      = YES
+ CALL_GRAPH             = NO
+ CALLER_GRAPH           = NO
+-GRAPHICAL_HIERARCHY    = NO
+-DIRECTORY_GRAPH        = NO
++GRAPHICAL_HIERARCHY    = YES
++DIRECTORY_GRAPH        = YES
+ DOT_IMAGE_FORMAT       = png
+-INTERACTIVE_SVG        = NO
++INTERACTIVE_SVG        = YES
+ DOT_PATH               =
+ DOTFILE_DIRS           =
+ MSCFILE_DIRS           =
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P05_TicTacToe/P05_TicTacToe.html b/build/html/P05_TicTacToe/P05_TicTacToe.html new file mode 100644 index 0000000..10394d6 --- /dev/null +++ b/build/html/P05_TicTacToe/P05_TicTacToe.html @@ -0,0 +1,424 @@ + + + + + + + + + 05 - SNP: TicTacToe — SNP Labs documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

05 - SNP: TicTacToe

+P05_TicTacToe/zhaw_neg_P2945.jpg + +
+

Übersicht

+

In diesem Praktikum erweitern Sie einen Programm Rahmen zu einem funktionierenden TicTacToe Spiel. Bei TicTacToe legen zwei Spieler abwechselnd auf einem 3x3 Brett einen Stein, bis ein Spieler mit einer horizontalen, vertikalen oder diagonalen Linie gewinnt, oder alle Felder besetzt sind.

+../_images/TicTacToe.svg

In der Aufgabe implementieren Sie die fehlenden Funktionen bis alle Tests erfolgreich durchlau-fen. Die gewählte Vorgehensweise ist somit TDD – Test-Driven-Development: es existieren zuerst Tests welche alle fehlschlagen. Schrittweise werden die Funktionen implementiert bis alle Tests erfolgreich durchlaufen.

+

Wenn die Tests erfolgreich durchlaufen, wird auch das Programm funktionieren und Sie können jemanden mit dem Spiel herausfordern 😉.

+
+
+

Lernziele

+

In diesem Praktikum lernen Sie den Zugriff auf Arrays.

+
+
    +
  • Sie können anhand einer Beschreibung im Code die fehlenden Funktionen implementieren wo auf Arrays zugegriffen wird.

  • +
+
+

Die Bewertung dieses Praktikums ist am Ende angegeben.

+

Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind.

+
+
+

Aufgabe: TicTacToe

+

Das zu ergänzende Programm tic-tac-toe hat folgende Funktionalität: +#. es stellt ein 3x3 TicTacToe Spielbrett auf dem Terminal dar +#. es liest von stdin eine Ziffer 0…9 ein, wobei 0 für Programm-Terminieren, die übrigen Ziffern für die Wahl eines Feldes stehen +#. der erste Spielzug wird von Spieler A geführt, danach wechselt das Programm zwischen den Spielern A und B +# bei Gewinn oder bei vollem Brett ist das Spiel vorbei

+

Wenn die Aufgabe erfolgreich umgesetzt ist, können Sie das Spiel ausführen: +`bash +bin/tic-tac-toe +`

+

Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (make test).

+

Die Architektur des Programms folgt dem MVC – Model-View-Control Paradigma. Dieses Paradigma besagt, dass die View (Eingabe und Darstellung) über Control (Vermittler) das Modell (die eigentliche Programm-Logik) steuert und darstellt. Dabei sind folgende Abhängigkeiten gegeben:

+../_images/MVC_pattern.svg
+

Test-Driven-Development

+

Das Programm besteht aus folgenden Files:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Datei

ToDo

Makefile

    +
  • +
+

tests/tests.c

    +
  • +
+

src/main.c

    +
  • +
+

src/view.h

    +
  • +
+

src/view.c

    +
  • +
+

src/control.h

    +
  • +
+

src/control.c

    +
  • +
+

src/model.h

    +
  • +
+

src/model.c

siehe unten

+
    +
  1. Führen Sie make test aus:

  2. +
+
+

Suite: lab test +Test: test_model_init +init_model:… 0/0 FAILED

+
+
    +
  1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)

  2. +
+
+
+
Test: test_model_get_state …FAILED
    +
  1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)

  2. +
+
+
Test: test_model_get_winner …FAILED
    +
  1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)

  2. +
+
+
Test: test_model_can_move …FAILED
    +
  1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)

  2. +
+
+
Test: test_model_move …FAILED
    +
  1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)

  2. +
+
+
Test: test_model_get_win_line …FAILED
    +
  1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)

  2. +
+
+
Run Summary: Type Total Ran Passed Failed Inactive
+
+
suites 1 1 n/a 0 0

tests 6 6 0 6 0

+
+
+
+

asserts 6 6 0 6 n/a

+
+
+
+

2. Konzentrieren Sie sich auf den ersten Test der fehlschlägt. Dies ist ein Unit Test, welcher die Funktion model_init() prüft. Suchen Sie die Funktion in src/model.h und src/model.c. +Was ist die geforderte Funktionalität und wie ist sie implementiert? +Suchen Sie die darin aufgerufene model_init() Funktion und implementieren Sie diese.

+
void model_init(model_t *instance) {
+assert(instance);
+// Instructions to the students:
+// set all fields of the board to model_state_none
+// BEGIN-STUDENTS-TO-ADD-CODE
+
+// END-STUDENTS-TO-ADD-CODE }
+
+
+
    +
  1. Führen Sie make test und korrigieren Sie obige Funktion, bis der Test nicht mehr fehlschlägt.

  2. +
+
+
+

3.2 test_model_init

+

Gehen Sie analog zur ersten Teilaufgabe vor: +1. Führen Sie make test aus. +2. Suchen Sie die Funktion model_get_state() in model.h und model.c. +3. Implementieren Sie die intern benutzte Funktion get_state() gemäss der Anleitung im Code.

+
model_state_t model_get_state(model_t *instance, model_pos_t pos)
+{
+assert(instance);
+assert_pos(pos);
+
+// Instructions to the students:
+// replace the stub implementation my access to the field at the given position.
+// BEGIN-STUDENTS-TO-ADD-CODE
+
+
+return model_state_none; // stub
+
+
+// END-STUDENTS-TO-ADD-CODE
+}
+
+
+
    +
  1. Führen Sie make test und korrigieren Sie, bis die beiden Tests nicht mehr fehlschlagen.

  2. +
+
+
+

3.3 test_model_get_state test_model_get_winner

+

Gehen Sie analog zur ersten Teilaufgabe vor: +1. Führen Sie make test aus. +2. Suchen Sie die Funktion model_get_state() in model.h und model.c. +3. Implementieren Sie die intern benutzte Funktion get_state() gemäss der Anleitung im Code.

+
model_state_t model_get_state(model_t *instance, model_pos_t pos)
+{
+assert(instance);
+assert_pos(pos);
+
+// Instructions to the students:
+// replace the stub implementation my access to the field at the given position.
+// BEGIN-STUDENTS-TO-ADD-CODE
+
+
+return model_state_none; // stub
+
+
+// END-STUDENTS-TO-ADD-CODE
+}
+
+
+
+
+

3.4 test_model_can_move

+

Gehen Sie analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion model_can_move().

+
int model_can_move(model_t *instance){
+assert(instance);
+if (model_get_winner(instance) == model_state_none) {
+// Instructions to the students:
+// scan all fields: return 1 with first field which equals model_state_none
+// BEGIN-STUDENTS-TO-ADD-CODE
+
+// END-STUDENTS-TO-ADD-CODE}
+return 0;}
+
+
+
+
+

3.5 test_model_move test_model_get_win_line

+

Schliesslich gehen Sie auch hier analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion set_state().

+
/**
+* @brief                    Sets the field on the board to the given state.
+* @param  instance [INOUT]  The instance which holds the state.
+* @param  pos      [IN]     The affected field.
+* @param  state    [IN]     The new state of the field.
+*/
+static void set_state(model_t *instance, model_pos_t pos, model_state_t state)
+{
+assert_pos(pos);
+
+// Instructions to the students:
+// set the field of the board to the new state
+// BEGIN-STUDENTS-TO-ADD-CODE
+
+// END-STUDENTS-TO-ADD-CODE
+}
+
+
+

Wenn die beiden obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch und das Spiel kann gespielt werden.

+
+
+
+

4. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Gewicht

TicTacToe

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

TicTacToe

Teilaufgabe test_model_init

1

TicTacToe

Teilaufgabe test_model_get_state und test_model_get_winner

1

TicTacToe

Teilaufgabe test_model_can_move

1

TicTacToe

Teilaufgabe test_model_move und test_model_get_win_line

1

+

Version: 15.02.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P05_TicTacToe/README.html b/build/html/P05_TicTacToe/README.html new file mode 100644 index 0000000..e72691c --- /dev/null +++ b/build/html/P05_TicTacToe/README.html @@ -0,0 +1,393 @@ + + + + + + + + + 05 - Arrays/Strings/TicTacToe — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

05 - Arrays/Strings/TicTacToe

+
+
+

1. Übersicht

+

In diesem Praktikum werden Sie in der ersten Aufgabe ein Programm zum Einlesen, Sortieren und Ausgeben von Strings von Grund auf entwickeln.

+

In der zweiten Aufgabe werden Sie einen Programmrahmen zu einem funktionierenden TicTacToe-Spiel erweitern. Sie implementieren hierbei die fehlenden Funktionen bis alle Tests erfolgreich durchlaufen. Die gewählte Vorgehensweise entspricht somit Test-Driven-Development (TDD). D.h. es existieren zuerst Tests, welche alle fehlschlagen. Schrittweise werden die Funktionen implementiert bis alle Tests erfolgreich durchlaufen. Wenn die Tests erfolgreich durchlaufen, wird auch das Programm funktionieren.

+
+
+
+

2. Lernziele

+

In diesem Praktikum schreiben Sie selbst von Grund auf ein C-Programme, das mit Strings operiert. Ferner ergänzen Sie ein bestehendes und lernen dabei den Zugriff auf Arrays.

+
    +
  • Sie können mit Arrays von Strings umgehen.

  • +
  • Sie können String-Funktionen aus der Standard Library verwenden.

  • +
  • Sie können anhand einer Beschreibung im Code die fehlenden Funktionen die auf Arrays zugreifen implementieren.

  • +
+
+
+
+

3. Aufgabe 1: Sortieren von Strings

+

Schreiben Sie ein C-Programm, das bis zu 10 Wörter mit einer maximalen Länge von jeweils 20 char von der Tastatur einliest, diese in Grossbuchstaben umwandelt, in einem Array der Reihe nach ablegt und zum Schluss im Array alphabetisch sortiert und ausgibt. Wiederholt eingegebene Wörter sollen dabei ignoriert werden. Das Ende der Eingabe soll durch das Erreichen der zehn unterschiedlichen Wörter oder durch die Eingabe von „ZZZ“ erfolgen. Die Ausgabe der sortierten Wörter soll direkt nach Beendigung der Eingabe erfolgen.

+

Hinweise:

+
    +
  • Zur Speicherung der Wörter sollten Sie ein zweidimensionales Array verwenden.

  • +
  • Verwenden Sie die String-Funktionen der C Standard Library (include <string.h>), z.B. um Strings alphabetisch zu vergleichen.

  • +
  • Wenn Sie aus anderen Vorlesungen bereits einen effizienten Sortieralgorithmus kennen, können Sie diesen natürlich verwenden. Sonst erfinden Sie einfach einen eigenen.

  • +
  • Strukturieren Sie das Programm durch geeignete Funktionen.

  • +
+
+
+
+

4. Aufgabe 2: TicTacToe

+

Das zu ergänzende Programm tic-tac-toe hat folgende Funktionalität:

+
    +
  1. es stellt ein 3x3 TicTacToe Spielbrett auf dem Terminal dar

  2. +
  3. es liest von stdin eine Ziffer 0…9 ein, wobei 0 für Programm-Terminieren, die übrigen Ziffern für die Wahl eines Feldes stehen

  4. +
  5. der erste Spielzug wird von Spieler A geführt, danach wechselt das Programm zwischen den Spielern A und B

  6. +
  7. bei Gewinn oder bei vollem Brett ist das Spiel vorbei

  8. +
+

Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind.

+

Wenn die Aufgabe erfolgreich umgesetzt ist, können Sie das Spiel ausführen:

+
bin/tic-tac-toe
+
+
+

+

Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (make test).

+

Die Architektur des Programms folgt dem MVC – Model-View-Control Paradigma. Dieses Paradigma besagt, dass die View (Eingabe und Darstellung) über Control (Vermittler) das Modell (die eigentliche Programm-Logik) steuert und darstellt. Dabei sind folgende Abhängigkeiten gegeben:

+

+
+
+

4.1 Teilaufgabe test_model_init

+

Das Programm besteht aus folgenden Files:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Datei

ToDo

Makefile

-> gegeben, d.h. nichts anzupassen

tests/tests.c

-> gegeben, d.h. nichts anzupassen

src/main.c

-> gegeben, d.h. nichts anzupassen

src/view.h

-> gegeben, d.h. nichts anzupassen

src/view.c

-> gegeben, d.h. nichts anzupassen

src/control.h

-> gegeben, d.h. nichts anzupassen

src/control.c

-> gegeben, d.h. nichts anzupassen

src/model.h

-> gegeben, d.h. nichts anzupassen

src/model.c

-> anzupassen: umsetzen gemäss den Angaben unten

+
    +
  1. Führen Sie make test aus

  2. +
+
Suite: lab test
+  Test: test_model_init ...
+                init_model:... 0/0 FAILED
+    1. tests/tests.c:62  - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
+  Test: test_model_get_state ...FAILED
+    1. tests/tests.c:62  - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
+  Test: test_model_get_winner ...FAILED
+    1. tests/tests.c:62  - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
+  Test: test_model_can_move ...FAILED
+    1. tests/tests.c:62  - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
+  Test: test_model_move ...FAILED
+    1. tests/tests.c:62  - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
+  Test: test_model_get_win_line ...FAILED
+    1. tests/tests.c:62  - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
+
+Run Summary:    Type  Total    Ran Passed Failed Inactive
+              suites      1      1    n/a      0        0
+               tests      6      6      0      6        0
+             asserts      6      6      0      6      n/a
+
+
+
    +
  1. Konzentrieren Sie sich auf den ersten Test der fehlschlägt. Dies ist ein Unit Test, welcher die Funktion model_init() prüft. Suchen Sie die Funktion in src/model.h und src/model.c.

  2. +
  3. Was ist die geforderte Funktionalität und wie ist sie implementiert?

  4. +
+

Suchen Sie die darin aufgerufene model_init() Funktion und implementieren Sie diese.

+
void model_init(model_t *instance)
+{
+    assert(instance);
+
+    // Instructions to the students:
+    // set all fields of the board to model_state_none
+    // BEGIN-STUDENTS-TO-ADD-CODE
+
+
+
+    // END-STUDENTS-TO-ADD-CODE
+}
+
+
+
+
    +
  1. Führen Sie make test und korrigieren Sie obige Funktion, bis der Test nicht mehr fehlschlägt.

  2. +
+
+
+
+

4.2 Teilaufgabe test_model_get_state und test_model_get_winner

+

Gehen Sie analog zur ersten Teilaufgabe vor:

+
    +
  1. Führen Sie make test aus.

  2. +
  3. Suchen Sie die Funktion model_get_state() in model.h und model.c.

  4. +
  5. Implementieren Sie die intern benutzte Funktion get_state() gemäss der Anleitung im Code.

  6. +
+
model_state_t model_get_state(model_t *instance, model_pos_t pos)
+{
+    assert(instance);
+    assert_pos(pos);
+
+    // Instructions to the students:
+    // replace the stub implementation my access to the field at the given position.
+    // BEGIN-STUDENTS-TO-ADD-CODE
+
+
+    return model_state_none; // stub
+
+
+    // END-STUDENTS-TO-ADD-CODE
+}
+
+
+
+
+
+
+
+

4.3 Teilaufgabe test_model_can_move

+

Gehen Sie analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion model_can_move().

+
int model_can_move(model_t *instance)
+{
+    assert(instance);
+    if (model_get_winner(instance) == model_state_none) {
+        // Instructions to the students:
+        // scan all fields: return 1 with first field which equals model_state_none
+        // BEGIN-STUDENTS-TO-ADD-CODE
+
+
+
+        // END-STUDENTS-TO-ADD-CODE
+    }
+    return 0;
+}
+
+
+
+
+
+
+

4.4 Teilaufgabe test_model_move und test_model_get_win_line

+

Schliesslich gehen Sie auch hier analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion set_state().

+
/**
+ * @brief                    Sets the field on the board to the given state.
+ * @param  instance [INOUT]  The instance which holds the state.
+ * @param  pos      [IN]     The affected field.
+ * @param  state    [IN]     The new state of the field.
+ */
+static void set_state(model_t *instance, model_pos_t pos, model_state_t state)
+{
+    assert_pos(pos);
+
+    // Instructions to the students:
+    // set the field of the board to the new state
+    // BEGIN-STUDENTS-TO-ADD-CODE
+
+
+
+
+    // END-STUDENTS-TO-ADD-CODE
+}
+
+
+
+

Wenn die beiden obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch und das Spiel kann gespielt werden.

+
+
+
+
+

5. Bewertung

+

Der funktionierende Programmcode muss der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

Sortieren von Strings

Sie können das funktionierende Programm demonstrieren und erklären.

2

TicTacToe

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

TicTacToe

Teilaufgabe test_model_init

0.5

TicTacToe

Teilaufgabe test_model_get_state und test_model_get_winner

0.5

TicTacToe

Teilaufgabe test_model_can_move

0.5

TicTacToe

Teilaufgabe test_model_move und test_model_get_win_line

0.5

+
+

Version: 14.02.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P06_Personen_Verwaltung_Linked_List/README.html b/build/html/P06_Personen_Verwaltung_Linked_List/README.html new file mode 100644 index 0000000..436dae5 --- /dev/null +++ b/build/html/P06_Personen_Verwaltung_Linked_List/README.html @@ -0,0 +1,394 @@ + + + + + + + + + 06 - Personen Verwaltung – Linked List — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

06 - Personen Verwaltung – Linked List

+
+

+
+
+

1. Übersicht

+

In diesem Praktikum schreiben Sie eine einfache Personenverwaltung. Dabei werden Sie etliche Elemente von C anwenden:

+
    +
  • Header Files selber schreiben, inklusive Include Guard

  • +
  • Typen definieren

  • +
  • Funktionen mit by value und by reference Parametern deklarieren und definieren

  • +
  • einfache Variablen, Pointer Variablen, struct Variablen und Array Variablen benutzen

  • +
  • Strukturen im Speicher dynamisch allozieren und freigeben

  • +
  • I/O und String Funktionen aus der Standard Library anwenden

  • +
  • Anwender Eingaben verarbeiten

  • +
  • Fehlerbehandlung

  • +
+
+
+
+

2. Lernziele

+

In diesem Praktikum wenden Sie viele der bisher gelernten C Elemente an.

+
    +
  • Sie können anhand dieser Beschreibung ein vollständiges C Programm schreiben.

  • +
  • Sie können Unit Tests schreiben welche die wesentlichen Funktionen des Programms individuell testen.

  • +
  • +
+

Die Bewertung dieses Praktikums ist am Ende angegeben.

+

Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind.

+
+
+
+

3. Personenverwaltung

+
+
+

3.1 Programmfunktion

+

Das Programm soll in einer Schleife dem Benutzer jeweils folgende Auswahl bieten, wovon eine Aktion mit Eingabe des entsprechenden Buchstabens ausgelöst wird:

+

I(nsert), R(emove), S(how), C(lear), E(nd):

+
    +
  • Insert: der Benutzer wird aufgefordert, eine Person einzugeben

  • +
  • Remove: der Benutzer wird aufgefordert, die Daten einer zu löschenden Person einzu-geben

  • +
  • Show: eine komplette Liste aller gespeicherten Personen wird in alphabetischer Rei-henfolge ausgegeben

  • +
  • Clear: alle Personen werden gelöscht

  • +
  • End: das Programm wird beendet

  • +
+
+
+
+

3.2 Designvorgaben

+

Verkettete Liste +Da zur Kompilierzeit nicht bekannt ist, ob 10 oder 10’000 Personen eingegeben werden, wäre es keine gute Idee, im Programm einen statischen Array mit z.B. 10’000 Personen-Einträgen zu allozieren. Dies wäre ineffizient und umständlich beim sortierten Einfügen von Personen. In solchen Situationen arbeitet man deshalb mit dynamischen Datenstrukturen, die zur Laufzeit beliebig (solange Speicher vorhanden ist) wachsen und wieder schrumpfen können. Eine sehr populäre dynamische Datenstruktur ist die verkettete Liste und genau die werden wir in diesem Praktikum verwenden.

+

+

+

+

+Abbildung 1: Zyklisch verkettete Liste

+

Eine verkettete Liste bedeutet, dass ein Knoten der verketten Liste einen Datensatz einer Person speichert und zusätzlich einen Pointer auf den nächsten Knoten in der Liste aufweist (siehe Abbildung 1). In dieser Pointer Variablen (next in der node_t Struktur unten) steht also einfach die Adresse des nächsten Knotens.

+

Die leere Liste besteht aus einem einzelnen Element, welches keine spezifische Person abspeichert und welches auf sich selbst zeigt (Abbildung 1 a). Dieses Element ist der Einstiegspunkt der Liste (auch Anker oder Wurzel genannt) und ist das einzige Element, das Sie im Programm direkt kennen und einer Variablen zuweisen. Dieses Element können Sie statisch allozieren (z.B. node_t anchor;, siehe Details weiter unten), denn es existiert während der gesamten Ausführungszeit. Alle anderen Elemente erreichen Sie ausgehend vom Anker, indem Sie einmal, den Pointern folgend, im Kreis herum gehen. Abbildung 1 b zeigt die Liste nach dem Einfügen der Person Max Mueller, 40 Jahre. Nach dem Einfügen von zwei weiteren Personen sieht die Datenstruktur aus wie in Abbildung 1 c. Das Entfernen der Person Arno Bosshard führt zu Abbildung 1 d.

+

Eine Person kann zugefügt werden, indem dynamisch ein neuer Knoten erzeugt wird und dieser in die verkettete Liste eingefügt wird. Beim Einfügen müssen die Adressen der Knoten so den Pointern zugewiesen werden, dass die Kette intakt bleibt.

+

Ein Knoten wird entfernt, indem der entsprechende Knoten aus der Verkettung herausgelöst wird (next des Vorgängerknotens soll neu auf next des herauszulösenden Knotens zeigen) und dann der Speicher des entsprechenden Knotens freigegeben wird.

+

Personen und Knoten Records

+

Die für je eine Person zu speichernden Daten sollen in folgendem C struct zusammengefasst sein.

+
#define NAME_LEN 20
+
+typedef struct {
+  char         name[NAME_LEN];
+  char         first_name[NAME_LEN];
+  unsigned int age;
+} person_t;
+
+
+

Jeder Knoten der verketteten Liste soll aus folgendem C struct bestehen.

+
typedef struct node {
+  person_t     content;        // in diesem Knoten gespeicherte Person
+  struct node *next;           // Pointer auf den nächsten Knoten in der Liste
+} node_t;
+
+
+

Vorschlag: zyklisch verkettete Liste

+

Erkennen des Endes der Liste: bei der zyklisch verketteten Liste zeigt das letzte Element wie-der auf den Anker, die Liste bildet also einen Kreis. Dies ist in Abbildung 1 so abgebildet.

+

Alternativ könnte man das Ende erkennbar machen, indem die Kette anstelle von zyklisch, mit einem NULL Pointer endet.

+

Die Wahl ist ihnen überlassen ob sie die eine oder andere Art der End-Erkennung implementieren. In der Beschreibung wird angenommen, dass es sich um eine zyklisch verkettete Liste handelt.

+

Sortiertes Einfügen

+

Die Personen Records sollen sortiert in die Liste eingefügt werden. Dies bedeutet, dass vom Anker her gesucht werden soll, bis der erste Knoten gefunden wurde dessen Nachfolgeknoten entweder „grösser“ ist als der einzufügende Knoten, oder wo das Ende der Liste erreicht ist. Die Ordnung (grösser, gleich, kleiner) soll so definiert sein:

+
// if (p1 > p2) { ... }
+if (person_compare(&p1, &p2) > 0) { ... }
+/**
+ * @brief  Compares two persons in this sequence: 1st=name, 2nd=first_name, 3rd=age
+ * @param  a [IN] const reference to 1st person in the comparison
+ * @param  b [IN] const reference to 2nd person in the comparison
+ * @return =0 if all record fields are the same
+ *         >0 if all previous fields are the same, but for this field, a is greater
+ *         <0 if all previous fields are the same, but for this field, b is greater
+ * @remark strncmp() is used for producing the result of string field comparisons
+ * @remark a->age – b->age is used for producing the result of age comparison
+ */
+int person_compare(const person_t *a, const person_t *b);
+
+
+

Eingabe

+

Fehlerhafte Wahl der Operation in der Hauptschleife soll gemeldet werden, ansonsten aber ignoriert werden.

+

Fehlerhafte Eingabe der Personenangaben sollen gemeldet werden und die gesamte Operation (z.B. Insert) verworfen werden.

+

Zu prüfende Fehler bei Personeneingaben:

+
    +
  • für die Namen

    +
      +
    • zu lange Namen

    • +
    +
  • +
  • für das Alter

    +
      +
    • keine Zahl

    • +
    +
  • +
  • Duplikat

    +
      +
    • derselbe Record soll nicht doppelt in der Liste vorkommen

    • +
    +
  • +
+

Weitergehende Prüfungen sind nicht erwartet.

+

Zu beachten: bei fehlerhafter Eingabe darf kein „Memory Leak“ entstehen, d.h. potentiell auf dem Heap allozierter Speicher muss im Fehlerfall freigegeben werden.

+
+
+
+

3.3 Bestehender Programmrahmen

+

Der Programmrahmen besteht aus den unten aufgelisteten Files. Es sollen weitere Module in src hinzugefügt werden und die bestehenden Files ergänzt werden gemäss den Aufgaben.

+ + + + + + + + + + + + + + + + + +

Makefile

-> zu ergänzen mit neuen Modulen

tests/tests.c

-> zu ergänzen gemäss Aufgaben (implementieren von Unit Tests)

src/main.c

-> zu ergänzen gemäss Aufgaben (Hauptprogramm)

+
+
+
+
+

4. Aufgabe 1: Modularisierung – API und Implementation main.c

+

Kreieren Sie folgende Files in src und implementieren Sie main.c basierend auf dem unten von Ihnen gegebenen API.

+

File person.h

+

Typ Definitionen:

+
person_t... // siehe Beschreibung oben
+
+
+

Funktionsdeklarationen:

+
// siehe Beschreibung oben
+int person_compare(const person_t *a, const person_t *b);
+
+
+
    +
  • gegebenenfalls weitere Funktionen für die Bearbeitung von Personen

  • +
+

File list.h

+

Typ Definitionen:

+
person_t... // siehe Beschreibung oben
+
+
+

Funktionsdeklarationen:

+
    +
  • Funktionen für insert, remove, clear Operationen auf der Liste

  • +
+
+

Das Hauptprogramm soll die Eingabeschleife implementieren und die obigen Funktionen (wo angebracht) aufrufen.

+
+
+
+

5. Aufgabe 2: Implementierung von person.c und list.c

+

Fügen Sie die beiden Implementationsfiles person.c und list.c zu src. Fügen Sie die beiden Module im Makefile zu der vorgegebenen Variablen MODULES hinzu, so dass sie beim make Aufruf auch berücksichtigt werden.

+
+
+

5.1 Teilaufgabe: Implementierung von person.c

+

Implementieren Sie die Funktionen aus person.h.

+

Falls nötig, stellen Sie weitere statische Hilfsfunktionen in person.c zur Verfügung.

+
+
+
+

5.2 Teilaufgabe: Implementierung von list.c

+

Implementieren Sie die Funktionen aus list.h.

+

Falls nötig, stellen Sie weitere statische Hilfsfunktionen in list.c zur Verfügung.

+
+
+
+
+

6. Aufgabe 3: Unit Tests

+

Schreiben Sie Unit Tests für mindestens die folgenden Funktionen

+
    +
  • person.h:

    +
      +
    • person_compare

    • +
    +
  • +
  • list.h:

    +
      +
    • list_insert

    • +
    • list_remove

    • +
    • list_clear

    • +
    +
  • +
+

Es existieren in tests/tests.c schon vier Test Rahmen für diese Test Cases.

+

In diese Test Cases sollen die entsprechenden Funktionen unter verschiedenen Bedingungen isoliert aufgerufen werden und deren Verhalten überprüft werden.

+

Verwenden Sie für die Überprüfung die CUnit CU_ASSERT_... Makros.

+

Siehe dazu auch man CUnit.

+

Wenn die obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch.

+
+
+
+

7. Bewertung

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

1

API von list.h und person.h plus die Implementation von main.c

2

2

Teilaufgabe: person.c

2

2

Teilaufgabe: list.c

2

3

Unit Tests

2

+
+

Version: 11.01.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P07_Prozesse_und_Threads/README.html b/build/html/P07_Prozesse_und_Threads/README.html new file mode 100644 index 0000000..0661cbe --- /dev/null +++ b/build/html/P07_Prozesse_und_Threads/README.html @@ -0,0 +1,646 @@ + + + + + + + + + 07 - Prozesse und Threads — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

07 - Prozesse und Threads

+
+

+

Quelle: https://www.wikiwand.com/de/Ein-Mann-Orchester

+
+
+

1. Übersicht

+

In diesem Praktikum werden wir uns mit Prozessen, Prozesshierarchien und Threads beschäftigen, um ein gutes Grundverständnis dieser Abstraktionen zu erhalten. Sie werden bestehenden Code analysieren und damit experimentieren. D.h. dies ist nicht ein «Codierungs»-Praktikum, sondern ein «Analyse»- und «Experimentier»-Praktikum.

+
+
+

1.1 Nachweis

+

Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.

+

Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman.

+
+
+
+
+

2. Lernziele

+

In diesem Praktikum werden Sie sich mit Prozessen, Prozesshierarchien und Threads beschäftigen. Sie erhalten einen vertieften Einblick und Verständnis zur Erzeugung, Steuerung und Terminierung von Prozessen unter Unix/Linux und Sie werden die unterschiedlichen Eigenschaften von Prozessen und Threads kennenlernen.

+
    +
  • Sie können Prozesse erzeugen und die Prozesshierarchie erklären

  • +
  • Sie wissen was beim Erzeugen eines Prozesses vom Elternprozess vererbt wird

  • +
  • Sie wissen wie man auf die Terminierung von Kindprozessen wartet

  • +
  • Sie kennen die Unterschiede zwischen Prozessen und Threads

  • +
+
+
+
+

3. Aufgaben

+

Das Betriebssystem bietet Programme um die aktuellen Prozesse und Threads darzustellen.

+

Die Werkzeuge kommen mit einer Vielzahl von Optionen für die Auswahl und Darstellung der Daten, z.B. ob nur Prozesse oder auch Threads aufgelistet werden sollen, und ob alle Prozesse oder nur die «eigenen» Prozesse ausgewählt werden sollen, etc.

+

Siehe die entsprechenden man Pages für weitere Details.

+

Eine Auswahl, welche unter Umständen für die folgenden Aufgaben nützlich sind:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +

ps

Auflisten der Prozess Zustände zum gegebenen Zeitpunkt.

pstree

Darstellung der gesamten Prozesshierarchie.

top

Wie ps, aber die Darstellung wird in Zeitintervallen aufdatiert.

htop

Wie top, aber zusätzlich dazu die Auslastung der CPU in einem System mit mehreren CPUs.

lscpu

Auflisten der CPUs.

cat/proc/cpuinfo

Ähnlich zu lscpu, aber mit Zusatzinformationen wie enthaltene CPU Bugs (z.B. bugs: cpu_meltdown spectre_v1 spect-re_v2 spec_store_bypass l1tf mds swapgs itlb_multihit)

+
+
+

3.1 Aufgabe 1: Prozess mit fork() erzeugen

+

Ziele

+
    +
  • Verstehen, wie mit fork() Prozesse erzeugt werden.

  • +
  • Einfache Prozesshierarchien kennenlernen.

  • +
  • Verstehen, wie ein Programm, das fork() aufruft, durchlaufen wird.

  • +
+

Aufgaben

+
    +
  1. Studieren Sie zuerst das Programm ProcA1.c und beschrieben Sie was geschieht.

    +
    
    +
    +
    +
  2. +
  3. Notieren Sie sich, was ausgegeben wird. Starten Sie das Programm und vergleichen Sie die Ausgabe mit ihren Notizen? Was ist gleich, was anders und wieso?

    +
    
    +
    +
    +
  4. +
+
+
+
+

3.2 Aufgabe 2: Prozess mit fork() und exec(): Programm Image ersetzen

+

Ziele

+
    +
  • An einem Beispiel die Funktion execl() kennenlernen.

  • +
  • Verstehen, wie nach fork() ein neues Programm gestartet wird. +Aufgaben

  • +
+
    +
  1. Studieren Sie zuerst die Programme ProcA2.c und ChildProcA2.c.

  2. +
  3. Starten Sie ProcA2.e und vergleichen Sie die Ausgabe mit der Ausgabe unter Aufgabe 1. Diskutieren und erklären Sie was gleich ist und was anders.

    +
    
    +
    +
    +
  4. +
  5. Benennen Sie ChildProcA2.e auf ChildProcA2.f um (Shell Befehl mv) und überlegen Sie, was das Programm nun ausgibt. Starten Sie ProcA2.e und vergleichen Sie Ihre Überlegungen mit der Programmausgabe.

    +
    
    +
    +
    +
  6. +
  7. Nennen Sie das Kindprogramm wieder ChildProcA2.e und geben Sie folgenden Befehl ein: chmod -x ChildProcA2.e. Starten Sie wiederum ProcA2.e und analysieren Sie die Ausgabe von perror("..."). Wieso verwenden wir perror()?

    +
    
    +
    +
    +
  8. +
+
+
+
+

3.3 Aufgabe 3: Prozesshierarchie analysieren

+

Ziele

+
    +
  • Verstehen, was fork() wirklich macht.

  • +
  • Verstehen, was Prozesshierarchien sind.

  • +
+

Aufgaben

+
    +
  1. Studieren Sie zuerst Programm ProcA3.c und zeichnen Sie die entstehende Prozesshierarchie (Baum) von Hand auf. Starten Sie das Programm und verifizieren Sie ob Ihre Prozesshierarchie stimmt.

  2. +
  3. Mit dem Befehl ps f oder pstree können Sie die Prozesshierarchie auf dem Bildschirm ausgeben. Damit die Ausgabe von pstree übersichtlich ist, müssen Sie in dem Fenster, wo Sie das Programm ProcA3.e starten, zuerst die PID der Shell erfragen, z.B. über echo $$. Wenn Sie nun den Befehl pstree -n -p pid-von-oben eingeben, wird nur die Prozesshierarchie ausgehend von der Bash Shell angezeigt: -n sortiert die Prozesse numerisch, -p zeigt für jeden Prozess die PID an.

  4. +
+

Hinweis: alle erzeugten Prozesse müssen arbeiten (d.h. nicht terminiert sein), damit die Darstellung gelingt. Wie wird das im gegebenen Programm erreicht?

+
+
+
+

3.4 Aufgabe 4: Zeitlicher Ablauf von Prozessen

+

Ziele

+
    +
  • Verstehen, wie Kind- und Elternprozesse zeitlich ablaufen.

  • +
+

Aufgaben

+
    +
  1. Studieren Sie Programm ProcA4.c. Starten Sie nun mehrmals hintereinander das Programm ProcA4.e und vergleichen Sie die jeweiligen Outputs (leiten Sie dazu auch die Ausgabe auf verschiedene Dateien um). Was schliessen Sie aus dem Resultat?

    +
    
    +
    +
    +
  2. +
+

Anmerkung: Der Funktionsaufruf selectCPU(0) erzwingt die Ausführung des Eltern- und Kindprozesses auf CPU 0 (siehe Modul setCPU.c). Die Prozedur justWork(HARD_WORK) simuliert CPU-Load durch den Prozess (siehe Modul workerUtils.c).

+
+
+
+

3.5 Aufgabe 5: Waisenkinder (Orphan Processes)

+

Ziele

+
    +
  • Verstehen, was mit verwaisten Kindern geschieht.

  • +
+

Aufgaben

+
    +
  1. Analysieren Sie Programm ProcA5.c: was läuft ab und welche Ausgabe erwarten Sie?

    +
    
    +
    +
    +
  2. +
  3. Starten Sie ProcA5.e: der Elternprozess terminiert: was geschieht mit dem Kind?

    +
    
    +
    +
    +
  4. +
  5. Was geschieht, wenn der Kindprozess vor dem Elternprozess terminiert? Ändern Sie dazu im sleep() Befehl die Zeit von 2 Sekunden auf 12 Sekunden und verfolgen Sie mit top das Verhalten der beiden Prozesse, speziell auch die Spalte S.

    +
    
    +
    +
    +
  6. +
+
+
+
+

3.6 Aufgabe 6: Terminierte, halbtote Prozesse (Zombies)

+

Ziele

+
    +
  • Verstehen, was ein Zombie ist.

  • +
  • Eine Möglichkeit kennenlernen, um Zombies zu verhindern.

  • +
+

Aufgaben

+
    +
  1. Analysieren Sie das Programm ProcA6.c.

  2. +
  3. Starten Sie das Script mtop bzw. mtop aaaa.e. Es stellt das Verhalten der Prozesse dynamisch dar.

    +

    Hinweis: <defunct> = Zombie.

    +
  4. +
  5. Starten Sie aaaa.e und verfolgen Sie im mtop-Fenster was geschieht. Was beachten Sie?

    +
    
    +
    +
    +
  6. +
  7. In gewissen Fällen will man nicht auf die Terminierung eines Kindes mit wait(), bzw. waitpid() warten. Überlegen Sie sich, wie Sie in diesem Fall verhindern können, dass ein Kind zum Zombie wird.

    +
    
    +
    +
    +
  8. +
+
+
+
+

3.7 Aufgabe 7: Auf Terminieren von Kindprozessen warten

+

Vorbemerkung: Diese Aufgabe verwendet Funktionen welche erst in der Vorlesung über Inter-Process-Communication (IPC) im Detail behandelt werden.

+

Sie können diese Aufgabe bis dann aufsparen oder die verwendeten Funktionen selber via man Pages im benötigten Umfang kennenlernen: man 2 kill und man 7 signal.

+

Ziele

+
    +
  • Verstehen, wie Informationen zu Kindprozessen abgefragt werden können.

  • +
  • Die Befehle wait() und waitpid() verwenden können.

  • +
+

Aufgaben

+
    +
  1. Starten Sie das Programm ProcA7.e und analysieren Sie wie die Ausgabe im Hauptprogramm zustande kommt und was im Kindprozess ChildProcA7.c abläuft.

    +
    
    +
    +
    +
  2. +
  3. Starten Sie ProcA7.e und danach nochmals mit 1 als erstem Argument. Dieser Argument Wert bewirkt, dass im Kindprozess ein ”Segmentation Error” erzeugt wird, also eine Speicherzugriffsverletzung. Welches Signal wird durch die Zugriffsverletzung an das Kind geschickt? Diese Information finden Sie im Manual mit man 7 signal. Schalten Sie nun core dump ein (siehe README) und starten Sie ProcA7.e 1 erneut und analysieren Sie die Ausgabe.

  4. +
+

+
+
+

Hinweis: ein core Dump ist ein Abbild des Speichers z.B. zum Zeitpunkt, wenn das Programm abstürzt (wie oben mit der Speicher Zugriff Verletzung). Der Dump wird im File core abgelegt und kann mit dem gdb (GNU-Debugger) gelesen werden (siehe README). Tippen Sie nach dem Starten des Command Line UI des gdb where gefolgt von list ein, damit sie den Ort des Absturzes sehen. Mit quit verlassen Sie gdb wieder.

+
    +
  1. Wenn Sie ProcA7.e 2 starten, sendet das Kind das Signal 30 an sich selbst. Was geschieht?

    +
    
    +
    +
    +
  2. +
  3. Wenn Sie ProcA7.e 3 starten, sendet ProcA7.e das Signal SIGABRT (abort) an das Kind: was geschieht in diesem Fall?

    +
    
    +
    +
    +
  4. +
  5. Mit ProcA7.e 4 wird das Kind gestartet und terminiert nach 5 Sekunden. Analysieren Sie wie in ProcA7.e der Lauf- bzw. Exit-Zustand des Kindes abgefragt wird (siehe dazu auch man 3 exit).

    +
    
    +
    +
    +
  6. +
+
+
+
+

3.8 Aufgabe 8: Kindprozess als Kopie des Elternprozesses

+

Ziele

+
    +
  • Verstehen, wie Prozessräume vererbt werden.

  • +
  • Unterschiede zwischen dem Prozessraum von Eltern und Kindern erfahren.

  • +
+

Aufgaben

+
    +
  1. Analysieren Sie Programm ProcA8_1.c: was gibt das Programm aus?

    +
      +
    • Starten Sie ProcA8_1.e und überprüfen Sie Ihre Überlegungen.

    • +
    • Waren Ihre Überlegungen richtig? Falls nicht, was könnten Sie falsch überlegt haben?

    • +
    +
    
    +
    +
    +
  2. +
  3. Analysieren Sie Programm ProcA8_2.c: was gibt das Programm aus?

    +
      +
    • Starten Sie ProcA8_2.e und überprüfen Sie Ihre Überlegungen.

    • +
    • Waren Ihre Überlegungen richtig? Falls nicht, was könnten Sie falsch gemacht haben?

    • +
    • Kind und Eltern werden in verschiedener Reihenfolge ausgeführt: ist ein Unterschied ausser der Reihenfolge festzustellen?

    • +
    +
    
    +
    +
    +
  4. +
  5. Analysieren Sie Programm ProcA8_3.c und Überlegen Sie, was in die Datei AnyOutPut.txt geschrieben wird, wer schreibt alles in diese Datei (sie wird ja vor fork() geöffnet) und wieso ist das so?

    +
      +
    • Starten Sie ProcA8_3.e und überprüfen Sie Ihre Überlegungen.

    • +
    • Waren Ihre Überlegungen richtig? Falls nicht, wieso nicht?

    • +
    +
    
    +
    +
    +
  6. +
+
+
+
+

3.9 Aufgabe 9: Unterschied von Threads gegenüber Prozessen

+

Ziele

+
    +
  • Den Unterschied zwischen Thread und Prozess kennenlernen.

  • +
  • Problemstellungen um Threads kennenlernen.

  • +
  • Die pthread-Implementation kennen lernen.

  • +
+

Aufgaben

+
    +
  1. Studieren Sie Programm ProcA9.c und überlegen Sie, wie die Programmausgabe aussieht. Vergleichen Sie Ihre Überlegungen mit denjenigen aus Aufgabe 8.2 b) (Pro-cA8_2.e).

    +
      +
    • Starten Sie ProcA9.e und vergleichen das Resultat mit Ihren Überlegungen.

    • +
    • Was ist anders als bei ProcA8_2.e?

    • +
    +
  2. +
+

+
+
+
    +
  1. Setzen Sie in der Thread-Routine vor dem Befehl pthread_exit() eine unendliche Schleife ein, z.B. while(1) { }; .

    +
      +
    • Starten Sie das Programm und beobachten Sie das Verhalten mit top. Was beobachten Sie und was schliessen Sie daraus?

      +

      Hinweis: wenn Sie in top den Buchstaben H eingeben, werden die Threads einzeln dargestellt.

      +
    • +
    • Kommentieren Sie im Hauptprogram die beiden pthread_join() Aufrufe aus und starten Sie das Programm. Was geschieht? Erklären Sie das Verhalten.

    • +
    +
  2. +
+

+
+
+
+
+
+

3.10 Aufgabe 10 (optional):

+
+

3.10.1 Übersicht

+

Dieser Teil des Praktikums behandelt spezielle Prozesse: die Dämon Prozesse («daemon pro-cesses»). Es ist gedacht als Zusatz zum Basis Praktikum über Prozesse und Threads.

+

Auch dieser Teil ist ein «Analyse»- und «Experimentier»-Praktikum.

+
+
+
3.10.1.1 Nachweis
+

Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.

+

Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman.

+
+
+
+
+

3.10.2 Lernziele

+

In diesem Praktikum werden Sie sich mit Dämon Prozessen beschäftigen.

+
    +
  • Sie können die Problemstellung der Dämon Prozesse erklären

  • +
  • Sie können einen Dämon Prozess kreieren

  • +
  • Sie können aus dem Dämon Prozess mit der Umgebung kommunizieren

  • +
  • +
+
+
+
+

3.10.3 Aufgabe: Dämon Prozesse

+

Ziele

+
    +
  • Problemstellungen um Daemons kennenlernen:

    +
      +
    • wie wird ein Prozess zum Daemon?

    • +
    • wie erreicht man, dass nur ein Daemon vom gleichen Typ aktiv ist?

    • +
    • wie teilt sich ein Daemon seiner Umwelt mit?

    • +
    • wo “lebt” ein Daemon?

    • +
    +
  • +
+

Einleitung

+

Für diese Aufgabe haben wir einen Daemon implementiert: MrTimeDaemon gibt auf Anfrage die Systemzeit Ihres Rechners bekannt. Abfragen können Sie diese Zeit mit dem Programm WhatsTheTimeMr localhost. Die Kommunikation zwischen den beiden Prozessen haben wir mit TCP/IP Sockets implementiert. Weitere Infos zum Daemon finden Sie nach den Aufgaben.

+

Im Abschnitt 4 finden Sie Zusatzinformationen über diese Implementation eines Dämon Prozesses plus weiterführende Informationen.

+

Aufgaben

+
    +
  1. Für die folgende Aufgabe benötigen Sie mindestens zwei Fenster (Kommandozeilen-Konsolen). Übersetzen Sie die Programme mit make und starten Sie das Programm PlapperMaul in einem der Fenster. Das Programm schreibt (ca.) alle 0.5 Sekunden Hallo, ich bins…. Pidi plus seine Prozess-ID auf den Bildschirm. Mit dem Shell Befehl ps können Sie Ihre aktiven Prozesse auflisten, auch PlapperMaul. Überlegen Sie sich zuerst, was mit PlapperMaul geschieht, wenn Sie das Fenster schliessen: läuft PlapperMaul weiter? Was geschieht mit PlapperMaul wenn Sie sich ausloggen und wieder einloggen? Testen Sie Ihre Überlegungen, in dem Sie die entsprechenden Aktionen durchführen. Stimmen Ihre Überlegungen?

    +
    
    +
    +
    +
  2. +
  3. Starten Sie nun das Programm bzw. den Daemon MrTimeDaemon. Stellen Sie die gleichen Überlegungen an wie mit PlapperMaul und testen Sie wiederum, ob Ihre Überlegungen stimmen. Ob MrTimeDaemon noch läuft können Sie feststellen, indem Sie die Zeit abfragen oder den Befehl ps ajx | grep MrTimeDaemon eingeben: was fällt Ihnen am Output auf? Was schliessen Sie aus Ihren Beobachtungen?

    +
    
    +
    +
    +
  4. +
  5. Starten Sie MrTimeDaemon erneut, was geschieht?

    +
    
    +
    +
    +
  6. +
  7. Stoppen Sie nun MrTimeDaemon mit killall MrTimeDaemon.

  8. +
  9. Starten Sie MrTimeDaemon und fragen Sie mit WhatsTheTimeMr localhost oder mit WhatsTheTimeMr 127.0.0.1 die aktuelle Zeit auf Ihrem Rechner ab.

    +

    Optional: +Fragen Sie die Zeit bei einem Ihrer Kollegen ab. Dazu muss beim Server (dort wo MrTimeDaemon läuft) ev. die Firewall angepasst werden. Folgende Befehle müssen dazu mit root-Privilegien ausgeführt werden:

    +
    iptables-save > myTables.txt # sichert die aktuelle Firewall
    +iptables -I INPUT 1 -p tcp --dport 65534 -j ACCEPT
    +iptables -I OUTPUT 2 -p tcp --sport 65534 -j ACCEPT
    +
    +
    +

    Nun sollten Sie über die IP-Nummer oder über den Rechner-Namen auf den TimeServer mit WhatsTheTimeMr zugreifen können. +Die Firewall können Sie mit folgendem Befehl wiederherstellen:

    +
    iptables-restore myTables.txt
    +
    +
    +
  10. +
  11. Studieren Sie MrTimeDaemon.c, Daemonizer.c und TimeDaemon.c und analysieren Sie, wie die Daemonisierung abläuft. Entfernen Sie die Kommentare im Macro Out-PutPIDs am Anfang des Moduls Daemonizer.c. Übersetzen Sie die Programme mit make und starten Sie MrTimeDaemon erneut. Analysieren Sie die Ausgabe, was fällt Ihnen auf? Notieren Sie alle für die vollständige Daemonisierung notwendigen Schritte.

    +
    
    +
    +
    +
  12. +
  13. Setzen Sie beim Aufruf von Daemonizer() in MrTimeDaemon.c anstelle von lock-FilePath den Null-Zeiger NULL ein. Damit wird keine lock-Datei erzeugt. Übersetzen Sie die Programme und starten Sie erneut MrTimedaemon. Was geschieht bzw. wie können Sie feststellen, was geschehen ist?

    +

    Hinweis: lesen Sie das log-File: /tmp/timeDaemon.log.

    +
    
    +
    +
    +

    Wenn Sie noch Zeit und Lust haben: messen Sie die Zeit, zwischen Start der Zeitanfrage und Eintreffen der Antwort. Dazu müssen Sie die Datei WhatsTheTimeMr.c entsprechend anpassen.

    +
  14. +
+
+
+
+

3.10.4 Zusatzinformationen

+
+
+
3.10.4.1 Diese Implementation
+

Dieser Daemon besteht aus den 3 Komponenten.

+

Hauptprogramm: MrTimeDaemon.c

+

Hier werden die Pfade für die lock-Datei, die log-Datei und der ”Aufenthaltsort” des Daemons gesetzt. Die lock-Datei wird benötigt um sicherzustellen, dass der Daemon nur einmal gestartet werden kann. In die lock-Datei schreibt der Daemon z.B. seine PID und sperrt sie dann für Schreiben. Wird der Daemon ein zweites Mal gestartet und will seine PID in diese Datei schreiben, erhält er eine Fehlermeldung und terminiert (es soll ja nur ein Daemon arbeiten). Terminiert der Daemon, wird die Datei automatisch freigegeben. Weil Daemonen sämtliche Kontakte mit ihrer Umwelt im Normalfall abbrechen und auch kein Kontrollterminal besitzen, ist es sinnvoll, zumindest die Ausgabe des Daemons in eine log-Datei umzuleiten. Dazu stehen einige Systemfunktionen für Logging zur Verfügung. Der Einfachheit halber haben wir hier eine normale Datei im Verzeichnis /tmp gewählt.

+
+

Anmerkung: die Wahl des Verzeichnisses /tmp für die lock- und log-Datei ist für den normalen Betrieb problematisch, weil der Inhalt dieses Verzeichnisses jederzeit gelöscht werden kann, bzw. darf. Wir haben dieses Verzeichnis gewählt, weil wir die beiden Dateien nur für die kurze Zeit des Praktikums benötigen.

+
+

Der Daemon erbt sein Arbeitsverzeichnis vom Elternprozesse, er sollte deshalb in ein festes Verzeichnis des Systems wechseln, um zu verhindern, dass er sich in einem montierten (gemounteten) Verzeichnis aufhält, das dann beim Herunterfahren nicht demontiert werden könnte (wir haben hier wiederum /tmp gewählt).

+

Daemonizer: Daemonizer.c

+

Der Daemonizer macht aus dem aktuellen Prozess einen Daemon. Z.B. sollte er Signale (eine Art Softwareinterrupts) ignorieren: wenn Sie die CTRL-C Taste während dem Ausführen eines Vordergrundprozess drücken, erhält dieser vom Betriebssystem das Signal SIGINT und bricht seine Ausführung ab. Weiter sollte er die Dateierzeugungsmaske auf 0 setzen (Dateizugriffsrechte), damit kann er beim Öffnen von Dateien beliebige Zugriffsrechte verlangen (die Dateierzeugungsmaske erbt er vom Elternprozess). Am Schluss startet der Daemonizer das eigentliche Daemonprogramm: TimeDaemon.e.

+

Daemonprogramm: TimeDaemon.c

+

Das Daemonprogramm wartet in einer unendlichen Schleife auf Anfragen zur Zeit und schickt die Antwort an den Absender zurück. Die Datenkommunikation ist, wie schon erwähnt, mit Sockets implementiert, auf die wir aber im Rahmen dieses Praktikums nicht weiter eingehen wollen (wir stellen lediglich Hilfsfunktionen zur Verfügung).

+
+
+
+
3.10.4.2 Zusatzinformation zu Dämon Prozessen
+

Dämonen oder englisch Daemons sind eine spezielle Art von Prozessen, die vollständig unabhängig arbeiten, d.h. ohne direkte Interaktion mit dem Anwender. Dämonen sind Hintergrundprozesse und terminieren i.A. nur, wenn das System heruntergefahren wird oder abstürzt. Dämonen erledigen meist Aufgaben, die periodisch ausgeführt werden müssen, z.B. Überwachung von Systemkomponenten, abfragen, ob neue Mails angekommen sind, etc.

+

Ein typisches Beispiel unter Unix ist der Printer Daemon lpd, der periodisch nachschaut, ob ein Anwender eine Datei zum Ausdrucken hinterlegt hat. Wenn ja, schickt er die Datei auf den Drucker.

+

Hier wird eine weitere Eigenschaft von Daemons ersichtlich: meist kann nur ein Dämon pro Aufgabe aktiv sein: stellen Sie sich vor, was passiert, wenn zwei Druckerdämonen gleichzeitig arbeiten. Andererseits muss aber auch dafür gesorgt werden, dass ein Dämon wieder gestartet wird, falls er stirbt.

+
+
+
+
+
+
+
+

4. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

Sie können die gestellten Fragen erklären.

1

Prozess mit fork() erzeugen

0.5

2

Prozess mit fork() und exec(): Programm Image ersetzen

0.5

3

Prozesshierarchie analysieren

0.5

4

Zeitlicher Ablauf von Prozessen

0.5

5

Waisenkinder (Orphan Processes)

0.5

6

Terminierte, halbtote Prozesse (Zombies)

0.5

7

Auf Terminieren von Kindprozessen warten

0.5

8

Kindprozess als Kopie des Elternprozesses

0.5

9

Unterschied von Threads gegenüber Prozessen

0.5

10

Dämon Prozesse

(4)

+
+

Version: 11.01.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P07_Prozesse_und_Threads/README_P02.html b/build/html/P07_Prozesse_und_Threads/README_P02.html new file mode 100644 index 0000000..1237156 --- /dev/null +++ b/build/html/P07_Prozesse_und_Threads/README_P02.html @@ -0,0 +1,264 @@ + + + + + + + + + 09/02 - Dämon Prozesse — SNP Labs documentation + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

09/02 - Dämon Prozesse

+
+

+
+
+

1. Übersicht

+

Dieser Teil des Praktikums behandelt spezielle Prozesse: die Dämon Prozesse («daemon pro-cesses»). Es ist gedacht als Zusatz zum Basis Praktikum über Prozesse und Threads.

+

Auch dieser Teil ist ein «Analyse»- und «Experimentier»-Praktikum.

+
+
+

1.1 Nachweis

+

Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.

+

Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman.

+
+
+
+
+

2. Lernziele

+

In diesem Praktikum werden Sie sich mit Dämon Prozessen beschäftigen.

+
    +
  • Sie können die Problemstellung der Dämon Prozesse erklären

  • +
  • Sie können einen Dämon Prozess kreieren

  • +
  • Sie können aus dem Dämon Prozess mit der Umgebung kommunizieren

  • +
  • +
+
+
+
+

3. Aufgabe: Dämon Prozesse

+

Ziele

+
    +
  • Problemstellungen um Daemons kennenlernen:

    +
      +
    • wie wird ein Prozess zum Daemon?

    • +
    • wie erreicht man, dass nur ein Daemon vom gleichen Typ aktiv ist?

    • +
    • wie teilt sich ein Daemon seiner Umwelt mit?

    • +
    • wo “lebt” ein Daemon?

    • +
    +
  • +
+

Einleitung

+

Für diese Aufgabe haben wir einen Daemon implementiert: MrTimeDaemon gibt auf Anfrage die Systemzeit Ihres Rechners bekannt. Abfragen können Sie diese Zeit mit dem Programm WhatsTheTimeMr localhost. Die Kommunikation zwischen den beiden Prozessen haben wir mit TCP/IP Sockets implementiert. Weitere Infos zum Daemon finden Sie nach den Aufgaben.

+

Im Abschnitt 4 finden Sie Zusatzinformationen über diese Implementation eines Dämon Prozesses plus weiterführende Informationen.

+

Aufgaben

+
    +
  1. Für die folgende Aufgabe benötigen Sie mindestens zwei Fenster (Kommandozeilen-Konsolen). Übersetzen Sie die Programme mit make und starten Sie das Programm PlapperMaul in einem der Fenster. Das Programm schreibt (ca.) alle 0.5 Sekunden Hallo, ich bins…. Pidi plus seine Prozess-ID auf den Bildschirm. Mit dem Shell Befehl ps können Sie Ihre aktiven Prozesse auflisten, auch PlapperMaul. Überlegen Sie sich zuerst, was mit PlapperMaul geschieht, wenn Sie das Fenster schliessen: läuft PlapperMaul weiter? Was geschieht mit PlapperMaul wenn Sie sich ausloggen und wieder einloggen? Testen Sie Ihre Überlegungen, in dem Sie die entsprechenden Aktionen durchführen. Stimmen Ihre Überlegungen?

  2. +
+

+
+
+
    +
  1. Starten Sie nun das Programm bzw. den Daemon MrTimeDaemon. Stellen Sie die gleichen Überlegungen an wie mit PlapperMaul und testen Sie wiederum, ob Ihre Überlegungen stimmen. Ob MrTimeDaemon noch läuft können Sie feststellen, indem Sie die Zeit abfragen oder den Befehl ps ajx | grep MrTimeDaemon eingeben: was fällt Ihnen am Output auf? Was schliessen Sie aus Ihren Beobachtungen?

  2. +
+

+
+
+
    +
  1. Starten Sie MrTimeDaemon erneut, was geschieht?

  2. +
+

+
+
+
    +
  1. Stoppen Sie nun MrTimeDaemon mit killall MrTimeDaemon.

  2. +
  3. Starten Sie MrTimeDaemon und fragen Sie mit WhatsTheTimeMr localhost oder mit WhatsTheTimeMr 127.0.0.1 die aktuelle Zeit auf Ihrem Rechner ab.

  4. +
+

Optional: +Fragen Sie die Zeit bei einem Ihrer Kollegen ab. Dazu muss beim Server (dort wo MrTimeDaemon läuft) ev. die Firewall angepasst werden. Folgende Befehle müssen dazu mit root-Privilegien ausgeführt werden:

+
iptables-save > myTables.txt # sichert die aktuelle Firewall
+iptables -I INPUT 1 -p tcp --dport 65534 -j ACCEPT
+iptables -I OUTPUT 2 -p tcp --sport 65534 -j ACCEPT
+
+
+

Nun sollten Sie über die IP-Nummer oder über den Rechner-Namen auf den TimeServer mit WhatsTheTimeMr zugreifen können. +Die Firewall können Sie mit folgendem Befehl wiederherstellen:

+
iptables-restore myTables.txt
+
+
+
    +
  1. Studieren Sie MrTimeDaemon.c, Daemonizer.c und TimeDaemon.c und analysieren Sie, wie die Daemonisierung abläuft. Entfernen Sie die Kommentare im Macro Out-PutPIDs am Anfang des Moduls Daemonizer.c. Übersetzen Sie die Programme mit make und starten Sie MrTimeDaemon erneut. Analysieren Sie die Ausgabe, was fällt Ihnen auf? Notieren Sie alle für die vollständige Daemonisierung notwendigen Schritte.

  2. +
+

+
+
+
    +
  1. Setzen Sie beim Aufruf von Daemonizer() in MrTimeDaemon.c anstelle von lock-FilePath den Null-Zeiger NULL ein. Damit wird keine lock-Datei erzeugt. Übersetzen Sie die Programme und starten Sie erneut MrTimedaemon. Was geschieht bzw. wie können Sie feststellen, was geschehen ist?

    +

    Hinweis: lesen Sie das log-File: /tmp/timeDaemon.log.

    +
  2. +
+

+
+
+

Wenn Sie noch Zeit und Lust haben: messen Sie die Zeit, zwischen Start der Zeitanfrage und Eintreffen der Antwort. Dazu müssen Sie die Datei WhatsTheTimeMr.c entsprechend anpassen.

+
+
+
+

4. Zusatzinformationen

+
+
+

4.1 Diese Implementation

+

Dieser Daemon besteht aus den 3 Komponenten.

+

Hauptprogramm: MrTimeDaemon.c

+

Hier werden die Pfade für die lock-Datei, die log-Datei und der ”Aufenthaltsort” des Daemons gesetzt. Die lock-Datei wird benötigt um sicherzustellen, dass der Daemon nur einmal gestartet werden kann. In die lock-Datei schreibt der Daemon z.B. seine PID und sperrt sie dann für Schreiben. Wird der Daemon ein zweites Mal gestartet und will seine PID in diese Datei schreiben, erhält er eine Fehlermeldung und terminiert (es soll ja nur ein Daemon arbeiten). Terminiert der Daemon, wird die Datei automatisch freigegeben. Weil Daemonen sämtliche Kontakte mit ihrer Umwelt im Normalfall abbrechen und auch kein Kontrollterminal besitzen, ist es sinnvoll, zumindest die Ausgabe des Daemons in eine log-Datei umzuleiten. Dazu stehen einige Systemfunktionen für Logging zur Verfügung. Der Einfachheit halber haben wir hier eine normale Datei im Verzeichnis /tmp gewählt.

+
+

Anmerkung: die Wahl des Verzeichnisses /tmp für die lock- und log-Datei ist für den normalen Betrieb problematisch, weil der Inhalt dieses Verzeichnisses jederzeit gelöscht werden kann, bzw. darf. Wir haben dieses Verzeichnis gewählt, weil wir die beiden Dateien nur für die kurze Zeit des Praktikums benötigen.

+
+

Der Daemon erbt sein Arbeitsverzeichnis vom Elternprozesse, er sollte deshalb in ein festes Verzeichnis des Systems wechseln, um zu verhindern, dass er sich in einem montierten (gemounteten) Verzeichnis aufhält, das dann beim Herunterfahren nicht demontiert werden könnte (wir haben hier wiederum /tmp gewählt).

+

Daemonizer: Daemonizer.c

+

Der Daemonizer macht aus dem aktuellen Prozess einen Daemon. Z.B. sollte er Signale (eine Art Softwareinterrupts) ignorieren: wenn Sie die CTRL-C Taste während dem Ausführen eines Vordergrundprozess drücken, erhält dieser vom Betriebssystem das Signal SIGINT und bricht seine Ausführung ab. Weiter sollte er die Dateierzeugungsmaske auf 0 setzen (Dateizugriffsrechte), damit kann er beim Öffnen von Dateien beliebige Zugriffsrechte verlangen (die Dateierzeugungsmaske erbt er vom Elternprozess). Am Schluss startet der Daemonizer das eigentliche Daemonprogramm: TimeDaemon.e.

+

Daemonprogramm: TimeDaemon.c

+

Das Daemonprogramm wartet in einer unendlichen Schleife auf Anfragen zur Zeit und schickt die Antwort an den Absender zurück. Die Datenkommunikation ist, wie schon erwähnt, mit Sockets implementiert, auf die wir aber im Rahmen dieses Praktikums nicht weiter eingehen wollen (wir stellen lediglich Hilfsfunktionen zur Verfügung).

+
+
+
+

4.2 Zusatzinformation zu Dämon Prozessen

+

Dämonen oder englisch Daemons sind eine spezielle Art von Prozessen, die vollständig unabhängig arbeiten, d.h. ohne direkte Interaktion mit dem Anwender. Dämonen sind Hintergrundprozesse und terminieren i.A. nur, wenn das System heruntergefahren wird oder abstürzt. Dämonen erledigen meist Aufgaben, die periodisch ausgeführt werden müssen, z.B. Überwachung von Systemkomponenten, abfragen, ob neue Mails angekommen sind, etc.

+

Ein typisches Beispiel unter Unix ist der Printer Daemon lpd, der periodisch nachschaut, ob ein Anwender eine Datei zum Ausdrucken hinterlegt hat. Wenn ja, schickt er die Datei auf den Drucker.

+

Hier wird eine weitere Eigenschaft von Daemons ersichtlich: meist kann nur ein Dämon pro Aufgabe aktiv sein: stellen Sie sich vor, was passiert, wenn zwei Druckerdämonen gleichzeitig arbeiten. Andererseits muss aber auch dafür gesorgt werden, dass ein Dämon wieder gestartet wird, falls er stirbt.

+
+
+
+
+

5. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

Sie können die gestellten Fragen erklären.

1

Dämon Prozesse

4

+
+

Version: 11.01.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P08_Sync/README.html b/build/html/P08_Sync/README.html new file mode 100644 index 0000000..1955062 --- /dev/null +++ b/build/html/P08_Sync/README.html @@ -0,0 +1,384 @@ + + + + + + + + + 08 - Synchronisationsprobleme — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

08 - Synchronisationsprobleme

+
+
+

1. Übersicht

+

+

Quelle: https://commons.wikimedia.org/wiki/File:Velgast-suedbahn.jpg

+

In diesem Praktikum lernen sie zuerst am Beispiel eines Kaffee-Automaten verschiedene grundlegende Synchronisationsprobleme kennen und mit Hilfe von Locks (Mutexes) und Semaphoren lösen:

+
    +
  • gegenseitiger Ausschluss mit einem Lock

  • +
  • Erzwingen einer einfachen Reihenfolge

  • +
  • Erzwingen einer erweiterten Reihenfolge

  • +
+

Im zweiten Teil werden sie auf Basis dieser Grundlagen ein komplexeres Synchronisationsproblem bearbeiten, diesmal am Beispiel von Bank Transaktionen.

+
+
+

1.1 Nachweis

+

Dieses Praktikum ist eine leicht abgewandelte Variante des Sync Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen.

+

Als Autor des BSY Praktikums ist genannt: M. Thaler.

+
+
+
+
+

2. Lernziele

+

In diesem Praktikum werden sie Synchronisationsprobleme lösen

+
    +
  • Sie wissen wie man systematisch Synchronisationsprobleme analysiert

  • +
  • Sie wissen wann ein potentieller Deadlock entstehen kann

  • +
  • Sie können Mutex mit Threads anwenden

  • +
  • Sie können Semaphoren mit Prozessen anwenden

  • +
+
+
+
+

3. Einführung

+

Das Lösen von Synchronisationsproblemen ist oft nicht einfach, weil Prozesse bzw. Threads gleichzeitig ablaufen, ihre Aktivitäten jedoch nach Vorgaben koordiniert werden müssen: man verliert schnell den Überblick. Systematisches Vorgehen mit Aufzeichnen der Abläufe und Synchronisationsbedingungen bewährt ich sich in diesem Fall.

+
+
+

3.1 Wie löst man Synchronisationsprobleme?

+

Gehen sie beim Lösen von Synchronisationsproblemen in folgenden Schritten vor:

+
    +
  • Schritt 1: Prozesse (Threads) der Problemstellung identifizieren.
    +Prozesse sind die Aktivitäten, die gleichzeitig ausgeführt werden. In diesem Sinne sind sie eigenständige Ausführungs-Einheiten, deren zeitliches Verhalten synchronisiert werden muss.

  • +
  • Schritt 2: Ausführungsschritte der einzelnen Prozesse (Threads) ermitteln.
    +Erstellen sie eine Liste mit einer Spalte für jeden Prozess. Notieren sie für jeden Prozess stichwortartig die wesentlichen Aktionen in der gewünschten zeitlichen Reihenfolge. Tragen sie noch keine Synchronisationsoperationen ein, sondern Texte wie warten auf Geld, etc. Übertragen sie anschliessend die Liste in einen Ablaufgraphen (Siehe Beispiel in Abbildung 1).

  • +
  • Schritt 3: Synchronisationsbedingungen ermitteln.
    +Eine Synchronisationsbedingung ist eine zeitliche Beziehung (Abhängigkeit) zwischen Aktionen verschiedener Prozesse, die für das korrekte Arbeiten erforderlich ist. Zeichnen sie diese Beziehungen mit Pfeilen in den Ablaufgraphen aus Schritt 2 ein (Siehe Abbildung 1).

  • +
  • Schritt 4: Benötigte Semaphore definieren.
    +Für jede Synchronisationsbedingung wird ein eigener Semaphor benötigt. Notieren sie für jeden Semaphor einen Namen und den Wert, mit dem er initialisiert werden muss.

  • +
  • Schritt 5: Prozesse mit Semaphore Operationen ergänzen.
    +Erweitern sie nun alle Prozesse aus Schritt 2 mit den notwendigen Semaphore Operati-onen (Siehe Pseudocode in Abbildung 1).

  • +
  • Schritt 6: Implementation.
    +Implementieren und testen sie das vollständige Programm.

  • +
+

+
coin   = sem_open(...,0);
+coffee = sem_open(...,0);
+
+
+

Ablaufgraph und Pseudocode für 2 Prozesse und zwei Semaphore
+

+
+
+
+

4. Der Kaffee-Automat

+

Als Beispiel verwenden wir einen Automaten, der Kaffee verkauft. Der Kunde muss zum Kauf eines Kaffees zuerst eine bzw. mehrere Münzen einwerfen und anschliessend den gewünsch-ten Kaffee wählen. Der Automat gibt dann das entsprechende Getränk aus.

+

Im ersten Beispiel werden der Automat und die Kunden mit Threads modelliert und tauschen Daten über gemeinsame Speichervariablen aus. Im zweiten und dritten Beispiel werden der Automat und die Kunden mit Prozessen modelliert, dabei wird der Ablauf mit Hilfe von Sema-phoren gesteuert bzw. erzwungen.

+

Hinweis: die Programme zu den folgenden Aufgaben können alle mit startApp.e gestartet werden. Dieses Programm startet und stoppt Threads und Prozesse, alloziert und dealloziert die Ressourcen (Mutexes, Semaphore).

+
+
+

4.1 Aufgabe: Mutual Exclusion

+

Greifen mehrere Threads (oder Prozesse) auf gemeinsame Daten zu, können sogenannte Race Conditions entstehen. Das Resultat ist in diesem Fall abhängig von der Reihenfolge, in der die Threads (Prozesse) ausgeführt werden.

+

Im vorliegenden Beispiel wirft der Kunde eine 1 Euro Münze ein und drückt anschliessend auf eine von zwei Kaffeewahltasten. Dabei wird die Anzahl Münzen (coinCount) und die gewählte Kaffeesorte (selCount1, selCount2) inkrementiert. Diese Variablen sind in der Datenstruktur cData abgelegt, auf die gemeinsam Kaffee-Automat und Kunden zugreifen können. Der Auto-mat überprüft, ob die Anzahl Münzen und die Anzahl der Kaffeewahlen gleich gross sind, falls nicht, wird eine Fehlermeldung ausgegeben und alle Zähler auf Null gesetzt.

+
+

Aufgaben

+
    +
  1. Übersetzen sie die Programme im Verzeichnis mutex mit make und starten sie den Kaffee-Automaten mit startApp.e mehrmals hintereinander. +Analysieren sie die Datenwerte in den Fehlermeldungen, beschreiben sie was die Gründe dafür sind bzw. sein können.

  2. +
  3. Schützen sie nun den Zugriff auf die gemeinsamen Daten mit einem Mutex so, dass alle Threads eine konsistente Sicht der Daten haben. +Wir haben für sie einen Mutex vorbereitet: die Datenstruktur cData enthält die Mutex-Variable mutex, die in startApp.c initialisiert wird. Die Funktionen für das Schliessen und das Öffnen des Mutex (Locks) aus der pthread Bibliothek sind:

  4. +
+
pthread mutex lock(&(cD->lock));
+
+
+
    +
  • und

  • +
+
pthread mutex unlock(&(cD->lock));
+
+
+

Überprüfen sie, ob der Kaffee-Automat nun keine Fehlermeldungen mehr ausgibt. Erhö-hen sie dazu auch die Anzahl Kunden CUSTOMERS in commonDefs.h, z.B. auf 10.

+
    +
  1. Im Thread des Kaffee-Automaten wird an verschiedenen Orten mehrmals auf die gemeinsamen Daten in cD zugegriffen. Wenn sie die gemeinsamen Daten in lokale Variablen kopieren und dann nur noch auf diese lokalen Variablen zugreifen würden, könn-ten sie dann auf die Synchronisation mit dem Mutex verzichten?

  2. +
  3. Wie oft kann ein einzelner Kunde einen Kaffee beziehen, bis der nächste Kunde an die Reihe kommt? Hier reicht eine qualitative Aussage.

  4. +
+
+
+
+

4.2 Aufgabe: Einfache Reihenfolge

+

Wie sie im ersten Beispiel festgestellt haben, verhindert ein Mutex zwar, dass Race Conditions auftreten, die Verarbeitungsreihenfolge der Threads lässt sich jedoch nicht beeinflussen und ist zufällig. +Im Folgenden soll eine erzwungene Verarbeitungsreihenfolge implementiert werden:

+
    +
  • Ein Kunde benutzt den Automat für einen Kaffeekauf exklusiv, d.h. alle Schritte des Kunden werden innerhalb eines Mutexes ausgeführt. Ist ein Kunde an der Reihe, wartet er bis der Automat bereit ist, wirft eine Münze ein, wartet auf den Kaffee und gibt anschlies-send den Automaten für den nächsten Kunden frei.

  • +
  • Der Automat meldet zuerst in einer Endlos-Schleife, dass er für die Geld-Eingabe bereit ist, wartet dann auf die Eingabe einer Münze, gibt den Kaffee aus und meldet anschliessend wieder, wenn er bereit ist, etc.

  • +
+

Für die Lösung dieses Problems benötigen wir Semaphore, die, im Gegensatz zu Mutexes, auch in verschiedenen Prozessen gesetzt bzw. zurückgesetzt werden dürfen. Den Kaffee-Automat und die Kunden implementieren wir mit Prozessen. sie finden die entsprechenden Prozesse im Verzeichnis basicSequence.

+
+

Aufgaben

+
    +
  1. Beschreiben sie den Kaffee-Automaten mit Hilfe der 6 Schritte aus Abschnitt 3 auf Papier, dokumentieren sie dabei alle Schritte schriftlich.

  2. +
  3. Implementieren sie nun den Kaffee-Automaten. Ergänzen sie dazu den coffeeTeller und den customer Prozess so mit vier Semaphoren, dass die vorgegebenen Ablaufbedingungen eingehalten werden. Mit welchen Werten müssen die Semaphore initialisiert werden? +Wir haben für sie vier Semaphore vorbereitet: Achtung, sie sind aber noch auskommentiert (siehe commonDefs.h und startApp.c. Die benötigten Semaphor-Funktionen aus der POSIX Bibliothek sind:

  4. +
+
sem_wait(&semaphor);
+
+
+

und

+
sem_post(&semaphor);
+
+
+

Analysieren sie die Ausgabe der Prozesse (mehrmals starten). Was fällt auf?

+
    +
  1. Gibt Ihr Programm den Output in der korrekten Reihenfolge aus? Falls nicht, wie könnte das gelöst werden?

  2. +
+
+
+
+

4.3 Aufgabe: Erweiterte Reihenfolge

+

Die Preise steigen dauernd … auch der Kaffee wird immer teurer, er kostet nun 3 Euro. Da der Automat nur 1 Euro Stücke annehmen kann, muss der Kunde 3 Münzen einwerfen. Erweitern sie die Prozesse aus Aufgabe 4.2 so, dass eine vordefinierte Anzahl Münzen eingegeben werden muss (die Anzahl Münzen ist in commonDefs.h als NUM_COINS definiert). Verwenden sie keine zusätzlichen Semaphore, sondern nutzen sie, dass wir Counting Semaphore verwenden. Die vordefinierten Prozesse finden sie im Verzeichnis advancedSequence.

+
+

Aufgabe

+
    +
  • Passen sie den coffeeTeller und den customer Prozess so an, dass der Kunde mehrere Münzen einwerfen muss, bis der Automat einen Kaffee ausgeben kann.

  • +
+

Hinweis: POSIX Semaphore sind Counting Semaphore, können aber nicht auf vordefinierte Werte gesetzt werden (ausser bei der Initialisierung). Abhilfe schafft hier das mehrmalige Aufrufen von sem_post(), z.B. in einer for-Schleife.

+
+
+
+

4.4 Zusammenfassung

+

Wir haben drei grundlegenden Typen von Synchronisationsproblemen kennen gelernt:

+
    +
  • Mutex nur ein Prozess bzw. Thread kann gleichzeitig auf gemeinsame Daten zugreifen.

    +
      +
    • Beispiel: entweder liest der Kaffee-Automat die Daten oder ein Kunde verändert sie.

    • +
    +
  • +
  • Einfache Reihenfolge ein Prozess wartet auf die Freigabe durch einen anderen Prozess.

    +
      +
    • Beispiel: der Kaffee-Automat wartet auf die Eingabe einer Münze.

    • +
    +
  • +
  • Erweiterte Reihenfolge ein Prozess wartet auf mehrere Freigaben durch einen anderen Pro-zess.

    +
      +
    • Beispiel: der Kaffee-Automat wartet auf die Eingabe von drei Münzen.

    • +
    +
  • +
+
+
+
+
+

5. International Banking

+

Die International Bank of Transfer (IBT) besitzt in 128 Ländern Filialen und stellt für 2048 spezielle Handels-Kunden in jeder Filiale ein Konto zur Verfügung. Gelder dieser Kunden werden dauernd zwischen den Filialen hin und her transferiert, dazu beschäftigt die Bank sogenannte Pusher. Pusher heben Geldbeträge von Konten in einer Filiale ab und buchen sie auf den entsprechenden Konten in irgendeiner (auch in der eigenen) Filiale wieder ein. Die Beträge liegen zwischen 1000 und 100’000 Dollar und werden zufällig ausgewählt, die Wahl der beiden Filialen ist ebenfalls zufällig.

+
+

5.1 Implementation

+

Im Folgenden arbeiten wir mit einer pthread-basierten Implementation der IBT, die Pusher werden dabei mit Threads implementiert. Die Filialen der Bank sind als Array von Strukturen implementiert, wobei pro Filiale ein Lock (branchLock) und ein Array von Konten (Accounts) definiert ist. Die Konten sind wiederum Strukturen mit dem Kontostand (account) und dem Lock (acntLock), siehe dazu auch den Source Code. Die Zugriffe auf die Gelder sind imple-mentiert (Funktionen withdraw(), deposit(), transfer()), aber nicht synchronisiert. +Hinweis: es ist von Vorteil hier mit mehreren CPUs zu arbeiten. Falls sie eine VM verwenden, setzen sie die Anzahl CPUs auf das Maximum.

+
+
+

5.2 Aufgabe: Konto Synchronisation

+
    +
  1. Wechseln sie ins Verzeichnis banking/a1, übersetzen sie das Programm und starten sie es mit dem Skript ./startApp. Analysieren und erklären sie die Resultate. Notie-ren sie sich zudem die Laufzeiten für 1, 2 und 4 Threads.

  2. +
  3. Synchronisieren sie die Kontenzugriffe so, dass möglichst viele Zugriffe gleichzeitig ausgeführt werden können und die Zugriffe atomar sind. Sie dürfen nur eines der beiden Locks branchLock bzw. acntLock verwenden: welches wählen sie und wieso? Be-gründen sie ihre Antwort und testen sie ihre Lösung.

  4. +
+
+
+

5.3 Aufgabe: Filialen Zugriff in Critical Section

+

Ihr Chef meint, dass es wohl aus Sicherheitsgründen besser wäre, sowohl die Filialen und die jeweiligen Kontenzugriffen zu ”locken”.

+
    +
  1. Wechseln sie ins Verzeichnis banking/a2 und kopieren sie banking.c aus Aufgabe 5.2. Implementieren sie diese zusätzlichen Anforderungen. Analysieren sie die Resultate. Was stellen sie fest im Vergleich mit den Resultaten aus der Aufgabe 5.2? Was raten sie ihrem Chef?

  2. +
  3. Ein Kollege meint, es wäre effizienter beim Abheben des Betrags zuerst das Konto zu locken und dann die Filiale, hingegen beim Einbuchen zuerst die die Filiale und dann das Konto. Was für eine Antwort geben sie ihrem Kollegen?Hinweis: falls sie nicht sicher sind: probieren sie es aus.

  4. +
+
+
+

5.4 Aufgabe: Refactoring der Synchronisation

+

Das International Banking Committe (IBC) erlässt neue Richtlinien, die unter anderem fordern, dass die Gesamtbilanz einer Bank über sämtliche Filialen zu jeder Zeit konsistent sein muss.

+
    +
  1. Erklären sie wieso die Implementationen aus Aufgabe 5.2 und 5.3 diese Anforderungen nicht erfüllen.

  2. +
  3. Ihr Entwicklungsteam kommt zum Schluss, dass den Pushern neu nur noch eine Funktion transfer() für die Überweisung von Beträgen zwischen den Filialen und Konten zur Verfügung gestellt werden darf. +Welche Locks bzw. welches Lock muss verwendet werden, damit die Forderung des IBC erfüllt werden kann? Wechseln sie ins Verzeichnis banking/a3 und ergänzen sie die Funktion transfer() in banking.c um die entsprechenden Lock-Funktionen. +Wichtiger +Hinweis: es darf kein neues Lock eingeführt werden und die Gesamtbilanz über sämtliche Filialen muss jederzeit konsistent sein.

  4. +
  5. Testen und analysieren sie das Programm und vergleichen sie die Resultate (Funktionalität, Laufzeit) mit den Lösungen aus Aufgabe 5.2 und 5.3. Notieren sie sich, was ihnen bei dieser Aufgabe wichtig erscheint.

  6. +
  7. +
+
+
+
+
+

6. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Gewicht

Sie können die gestellten Fragen erklären.

4

4.1 Aufgabe: Mutual Exclusion
4.2 Aufgabe: Einfache Reihenfolge
4.3 Aufgabe: Erweiterte Reihenfolge

4

5

5.2 Aufgabe: Konto Synchronisation
5.3 Aufgabe: Filialen Zugriff in Critical Section
5.4 Aufgabe: Refactoring der Synchronisation

4

+
+
+

Version: 18.08.2021

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P09_File_Operations/README.html b/build/html/P09_File_Operations/README.html new file mode 100644 index 0000000..516a27f --- /dev/null +++ b/build/html/P09_File_Operations/README.html @@ -0,0 +1,168 @@ + + + + + + + + + 09 - File Operations — SNP Labs documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

09 - File Operations

+
+
+
+

1. Übersicht

+
+
+
+

2. Lernziele

+
+
+
+

3. Aufgabe 1:

+
+
+
+

4. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

1

-

-

+
+

Version: 16.02.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/P10_IPC/README.html b/build/html/P10_IPC/README.html new file mode 100644 index 0000000..6b7dd8f --- /dev/null +++ b/build/html/P10_IPC/README.html @@ -0,0 +1,165 @@ + + + + + + + + + 10 - IPC — SNP Labs documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

10 - IPC

+
+
+

1. Übersicht

+
+
+
+

2. Lernziele

+
+
+
+

3. Aufgabe 1:

+
+
+
+

4. Bewertung

+

Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

+ + + + + + + + + + + + + + + + + +

Aufgabe

Kriterium

Punkte

Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.

1

-

-

+
+

Version: 16.02.2022

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/_images/135oALYhkYyXB2aG0F-qrwA.jpeg b/build/html/_images/135oALYhkYyXB2aG0F-qrwA.jpeg new file mode 100644 index 0000000..afd7142 Binary files /dev/null and b/build/html/_images/135oALYhkYyXB2aG0F-qrwA.jpeg differ diff --git a/build/html/_images/MVC_pattern.png b/build/html/_images/MVC_pattern.png new file mode 100644 index 0000000..408a4b6 Binary files /dev/null and b/build/html/_images/MVC_pattern.png differ diff --git a/build/html/_images/MVC_pattern.svg b/build/html/_images/MVC_pattern.svg new file mode 100644 index 0000000..152be7f --- /dev/null +++ b/build/html/_images/MVC_pattern.svg @@ -0,0 +1,80 @@ + + +Das Main Programm kombiniert dieKomponenten nach obigen Vorgabenund startet die Abarbeitung von Eingabenüber die View.Control kennt Model,Model aber nicht Control.View kennt Control,Control aber nicht ViewModelControlView diff --git a/build/html/_images/P04_Aufgabenstellung.png b/build/html/_images/P04_Aufgabenstellung.png new file mode 100644 index 0000000..b694a31 Binary files /dev/null and b/build/html/_images/P04_Aufgabenstellung.png differ diff --git a/build/html/_images/TicTacToe.png b/build/html/_images/TicTacToe.png new file mode 100644 index 0000000..728da54 Binary files /dev/null and b/build/html/_images/TicTacToe.png differ diff --git a/build/html/_images/TicTacToe.svg b/build/html/_images/TicTacToe.svg new file mode 100644 index 0000000..f6f4bef --- /dev/null +++ b/build/html/_images/TicTacToe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/build/html/_images/Wochentagsberechnung.jpg b/build/html/_images/Wochentagsberechnung.jpg new file mode 100644 index 0000000..cd3abd0 Binary files /dev/null and b/build/html/_images/Wochentagsberechnung.jpg differ diff --git a/build/html/_images/a.png b/build/html/_images/a.png new file mode 100644 index 0000000..925d567 Binary files /dev/null and b/build/html/_images/a.png differ diff --git a/build/html/_images/b.png b/build/html/_images/b.png new file mode 100644 index 0000000..1c7d260 Binary files /dev/null and b/build/html/_images/b.png differ diff --git a/build/html/_images/c.png b/build/html/_images/c.png new file mode 100644 index 0000000..0b42e10 Binary files /dev/null and b/build/html/_images/c.png differ diff --git a/build/html/_images/coffee_customer.png b/build/html/_images/coffee_customer.png new file mode 100644 index 0000000..37d6274 Binary files /dev/null and b/build/html/_images/coffee_customer.png differ diff --git a/build/html/_images/d.png b/build/html/_images/d.png new file mode 100644 index 0000000..43daab4 Binary files /dev/null and b/build/html/_images/d.png differ diff --git a/build/html/_images/daemon.png b/build/html/_images/daemon.png new file mode 100644 index 0000000..2dd1ae3 Binary files /dev/null and b/build/html/_images/daemon.png differ diff --git a/build/html/_images/dep_dot.png b/build/html/_images/dep_dot.png new file mode 100644 index 0000000..7cdd91a Binary files /dev/null and b/build/html/_images/dep_dot.png differ diff --git a/build/html/_images/ein_mann_orchester.png b/build/html/_images/ein_mann_orchester.png new file mode 100644 index 0000000..099e0eb Binary files /dev/null and b/build/html/_images/ein_mann_orchester.png differ diff --git a/build/html/_images/kalender-108_v-ARDFotogalerie.jpg b/build/html/_images/kalender-108_v-ARDFotogalerie.jpg new file mode 100644 index 0000000..9f221af Binary files /dev/null and b/build/html/_images/kalender-108_v-ARDFotogalerie.jpg differ diff --git a/build/html/_images/linked_list.png b/build/html/_images/linked_list.png new file mode 100644 index 0000000..ee01144 Binary files /dev/null and b/build/html/_images/linked_list.png differ diff --git a/build/html/_images/modularisieren_von_c_code.JPG b/build/html/_images/modularisieren_von_c_code.JPG new file mode 100644 index 0000000..44e8067 Binary files /dev/null and b/build/html/_images/modularisieren_von_c_code.JPG differ diff --git a/build/html/_images/random_number.png b/build/html/_images/random_number.png new file mode 100644 index 0000000..71b768c Binary files /dev/null and b/build/html/_images/random_number.png differ diff --git a/build/html/_images/sequence_graph.png b/build/html/_images/sequence_graph.png new file mode 100644 index 0000000..e551d22 Binary files /dev/null and b/build/html/_images/sequence_graph.png differ diff --git a/build/html/_images/synchronisationsprobleme.png b/build/html/_images/synchronisationsprobleme.png new file mode 100644 index 0000000..da2334c Binary files /dev/null and b/build/html/_images/synchronisationsprobleme.png differ diff --git a/build/html/_images/uebersicht.png b/build/html/_images/uebersicht.png new file mode 100644 index 0000000..cdb4272 Binary files /dev/null and b/build/html/_images/uebersicht.png differ diff --git a/build/html/_sources/P01_Erste_Schritte_mit_C/README.md.txt b/build/html/_sources/P01_Erste_Schritte_mit_C/README.md.txt new file mode 100644 index 0000000..add4b2d --- /dev/null +++ b/build/html/_sources/P01_Erste_Schritte_mit_C/README.md.txt @@ -0,0 +1,84 @@ +# 01 - Erste Schritte mit C + +___ +## 1. Übersicht + +In diesem Praktikum erstellen Sie mehrere kleine C-Programme, in denen Sie Input- und Output-Funktionen der C Standard Library verwenden. + +Arbeiten Sie in Zweiergruppen und diskutieren Sie ihre Lösungsansätze miteinander, bevor Sie diese umsetzen. + +Bevor Sie mit den Programmieraufgaben beginnen, setzen Sie eine virtuelle Maschine mit der vorbereiteten Praktikumsumgebung auf. + +___ +## 2. Lernziele +In diesem Praktikum schreiben Sie selbst von Grund auf einige einfache C-Programme und wenden verschiedene Kontrollstrukturen an. + +- Sie können mit *#include* Funktionen der C Standard Library einbinden +- Sie können mit *#define* Macros definieren und diese anwenden +- Sie wenden die *Input-* und *Output-Funktionen* von C an, um Tastatur-Input einzulesen und formatierte Ausgaben zu machen. +- Sie verwenden die Kommandozeile, um ihren Sourcecode in ein ausführbares Programm umzuwandeln. +- Sie wenden for-und while-Loops sowie if-then-else-Verzweigungen an. +- Sie setzen eine Programmieraufgabe selbständig in ein funktionierendes Programm um. + +___ +## 3. Aufgabe 1: virtuelle Maschine +Im Moodle-Kurs "Systemnahe Programmierung" finden Sie unter "Praktika" eine Installationsanleitung für die virtuelle Maschine, die wir Ihnen zur Verfügung stellen. Die virtuelle Maschine enthält ein Ubuntu Linux-Betriebssystem und die für das Praktikum benötigten Frameworks. + +Folgen sie der Anleitung, um die virtuelle Maschine auf ihrem Rechner zu installieren. + +___ +## 4. Aufgabe 2: Hello World +Schreiben Sie ein C-Programm, das "Hello World" auf die Standardausgabe schreibt. Verwenden Sie die printf-Funktion aus der Standard Library. In den Vorlesungsfolien finden Sie bei Bedarf eine Vorlage. + +Erstellen sie das Source-File mit einem beliebigen Editor, sie benötigen nicht unbedingt eine IDE. Speichern Sie das Source-File mit der Endung `.c`. + +Um ihr Source-File zu kompilieren, verwenden Sie den GNU Compiler auf der Kommandozeile: +``` sh +$> gcc hello.c +``` + +Der Compiler übersetzt ihr Programm in eine ausführbare Datei `a.out`, die Sie mit + +``` sh +$> ./a.out +``` + +ausführen können. Sie können den Namen der ausführbaren Datei wählen, indem Sie die Option `-o` verwenden: + +``` sh +$> gcc hello.c -o hello +``` + +erzeugt die ausführbare Datei `hello`. + +Verwenden Sie die Option `-Wall`, um alle Warnungen des Compilers auszugeben. Dies weist Sie auf allfällige Programmierfehler hin. + +___ +## 5. Aufgabe 3: Tabellenausgabe +Schreiben Sie ein Programm in C, das von `stdin` einen Umrechnungsfaktor zwischen CHF und Bitcoin einliest und danach eine Tabelle von Franken- und Bitcoin-Beträgen ausgibt. Die Tabelle soll sauber formatiert sein, z.B. so: +``` +Enter conversion rate (1.00 BTC -> CHF): 43158.47 + 200 CHF <--> 0.00463 BTC + 400 CHF <--> 0.00927 BTC + 600 CHF <--> 0.01390 BTC + 800 CHF <--> 0.01854 BTC + 1000 CHF <--> 0.02317 BTC + 1200 CHF <--> 0.02780 BTC + 1400 CHF <--> 0.03244 BTC + 1600 CHF <--> 0.03707 BTC +``` + +- Verwenden Sie eine Schleife und die `printf`-Funktion für die Tabellenausgabe +- Definieren Sie ein Makro `NUM_ROWS`, um an zentraler Stelle im Source-Code zu definieren, wie viele Einträge die Tabelle in der Ausgabe haben soll. +- Lesen Sie den Umrechnungsfaktor mit der `scanf`-Funktion als `double` von der Kommandozeile ein. + +___ +## 6. Aufgabe 4: Zeichen und Wörter zählen +Schreiben Sie ein C-Programm, welches die Zeichen und Wörter einer mit der Tastatur eingegebenen Zeile zählt. Wortzwischenräume sind entweder Leerzeichen (' ') oder Tabulatoren ('\t'). Die Eingabe der Zeile mit einem newline-character ('\n') abgeschlossen. Danach soll ihr Programm die Anzahl Zeichen und die Anzahl Wörter ausgeben und terminieren. +- Verwenden Sie die `char getchar(void)` Funktion aus der `stdio.h` Library, um die Zeichen einzeln einzulesen. Die Funktion `getchar` kehrt nicht gleich bei Eingabe des ersten Zeichens zurück, sondern puffert die Daten, bis die Eingabe einer kompletten Zeile mit Return abgeschlossen wird. Dann wird das erste Zeichen aus dem Puffer zurückgegeben und mit weiteren Aufrufen von getchar können die nachfolgenden Zeichen aus dem Puffer gelesen werden. Gibt `getchar` das Zeichen `\n` zurück, ist die Zeile komplett zurückgegeben und der Puffer ist wieder leer. +- Setzen Sie eine Schleife ein, die beim Zeichen '\n' terminiert. +- Benutzen Sie if-then-else-Strukturen um die Wörter zu zählen. + +___ +## 7. Bewertung +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. diff --git a/build/html/_sources/P02_Funktionen_Datentyp_enum/README.md.txt b/build/html/_sources/P02_Funktionen_Datentyp_enum/README.md.txt new file mode 100644 index 0000000..4c33a66 --- /dev/null +++ b/build/html/_sources/P02_Funktionen_Datentyp_enum/README.md.txt @@ -0,0 +1,228 @@ +# 02: Funktionen, Datentyp "enum" + +___ + +![](./random_number.png) + +(Copyright Bild: xkcd.com) + +___ +## 1. Übersicht + In diesem Praktikum sind zwei Themen im Fokus: Funktionen und der Datentyp enum. + + Funktionen sind der wesentlichste Bestandteil der C Programmierung welcher eine strukturierte Programmierung ermöglicht: + * Eine Funktion ein Teil eines C Codes, der eine spezielle Aufgabe ausführt. Sie kann aus dem Hauptprogramm, oder aus anderen Funktionen, aufgerufen werden. + * Jede Funktion besitzt einen eindeutigen Namen, eine eindeutige Signatur (Typen und Reihenfolge der Parameter) und einen Rückgabewert (int falls nichts angegeben wird). + * Eine Funktion kann Werte aus dem aufrufendem Kontext übernehmen und bei Bedarf einen Wert an den aufrufenden Kontext zurückliefern. +Beispiel einer Additions-Funktion: +``` +#include + +/* Funktionsdeklaration */ +int add(int a, int b); + +int main(void) { + int aa = 1, bb = 2, cc; + printf("%aa + %bb = %cc", aa, bb, add(aa, bb);); + return 0; +} + +/* Funktionsdefinition */ +int add(int a, int b) { + return a + b; +} +``` +Der Daten typt enum wird verwendet um die Lesbarkeit von Programmen zu erhöhen: + +Beispiel eines enum: +``` +enum Ampeln = {rot =1, gelb, gruen}; + +int main(void) { + Ampeln ampel1; + if (ampel1 == rot) {...} + return 0; +} +``` + +___ +## 2. Lernziele + +In diesem Praktikum lernen Sie Funktionen zu definieren und aufzurufen, sowie enum anzuwenden. + * Sie können ein Programm schreiben welches aus mehreren Funktionen besteht. + * Sie können Funktionen deklarieren, definieren und aufrufen. + * Sie können enum Typen definieren und deren Werte bestimmen und abfragen. + +___ +## 3. Aufgaben + +```{eval-rst} +.. figure:: kalender-108_v-ARDFotogalerie.jpg + :width: 600px + :name: kalender-108_v-ARDFotogalerie + :align: center +``` + +(Copyright Bild: www.planet-wissen.de) + +### 3.1 Aufgabe 1 Tage pro Monat +In der ersten Aufgabe berechnen Sie die Tag pro Monat einer beliebigen Kombination Monat / Jahr. +Erweitern Sie dazu das Programm um folgende Aspekte: +* Bereichsprüfung von Jahr und Monat +* Funktion istSchaltjahr, welche berechnet, ob das Jahr eine Schaljahr ist +* Funktion tageProMonat, welche die Anzahl Tage des gegebenen Monats und Jahres berechnet. + +Vorgaben: +* Die Funktion istSchaltjahr nimmt ein Integer (jahr) entgegen und gibt 1 im Falle eiens Schltjahres und 0 im andreren Fall zurück +* Die Funktion tageProMonat nimmt zwei integer (monat und jahr) entgegeben und gibt die Anzahl Tage als Integer zurück +* Die Jahreszahl, welche den Funktionen übergeben wird, muss überprüft werden und grösser gleich 1599 und kleiner als 10000 sein +* Der übergebene Monat muss grösser als 0 und kleine als 13 sein. + +Die Regeln für die Schaltjahrberechnung: +* Schaltjahre sind alle Jahre, die durch 4 teilbar sind. +* Eine Ausnahme bilden die Jahrhunderte (1600, 1700…). Diese sind keine Schltjahre. +* zu den 100er gibt es ebenfalls Ausnahmen: Diese sind immer Schaltjahre, wenn sie durch 400 teilbar sind +... also zum Beispiel 1600 ist eines, nicht jedoch 1700. Weiterführende Details finden Sie unter https://de.wikipedia.org/wiki/Gregorianischer_Kalender + +Gegeben ist die main Funktion des Programms. Ergänzen Sie die enum Definition und die fehlenden Funktionen: +* gibIntWert: Die Funktion soll einen Int Wert zurückgeben. Der Bereich, wie auch Fehleingaben sollen sollen berücksichtigt werden. (atoi unfd fgets sind hier hilfreich) +* istSchaltjahr: Die Funktion gibt 1 im Falle eines Schltjahr und o im anderen Falle zurück. +* tageProMonat: Die Funktion gibt den die Tage des Monats für das definierte Jahr zurück. Verwenden Sie die Switchanweisung , sowie den enum Datentypen + +``` +int main (int argc, char *argv[]) { + + int monat, jahr; + + // Monat einlesen und Bereich ueberpruefen + monat = gibIntWert("Monat", 1, 12); + jahr = gibIntWert("Jahr", 1600, 9999); + + // Ausgabe zum Test + printf("Monat: %d, Jahr: %d \n", monat, jahr); + + // Ausgabe zum Test (hier mit dem ternaeren Operator "?:") + printf("%d ist %s Schaltjahr\n", jahr, istSchaltjahr(jahr) ? "ein" : "kein"); + + // Ausgabe + printf("Der Monat %02d-%d hat %d Tage.\n", monat, jahr, tageProMonat(jahr, monat)); + + return 0; +} +``` + +Tipp: Angenommen Sie verwenden den enum month_t { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ }; +Dann können Sie im Programm direkt die Konstanten verwenden: +``` +if (m == 2) ... // schlecht lesbar +if (monat == 2) ... // besserer Variablenname +if (monat == FEB) ... // am besten lesbar +``` + + +Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (`make test`) +___ +### 3.2 Aufgabe 2 Bestimmen des Wochentags +Erweitern Sie das vorgegebene zweite Programm Gerüst an den bezeichneten Stellen so, dass das Programm von der Kommando Zeile ein Argument entgegennimmt, es auf Gültigkeit überprüft und schliesslich den Wochentag für das gegebene Datum berechnet und ausgibt. +Prüfen Sie die Umsetzung beider Teilaufgaben mittels make test. +#### 3.2.1 Teilaufgabe Argumente Parsen und auf Korrektheit prüfen +Das Argument stellt ein gültiges Datum unseres Gregorianischen Kalenders dar (d.h. ein Datum ab Donnerstag, den 15. Oktober 1582, mit der Gregorianischen Schaltjahr Regel). +Wenn kein Argument gegeben ist oder wenn das eingegebene Datum nicht gültig ist, soll das Programm einem Hilfetext auf stderr ausgeben und mit EXIT_FAILURE Exit Code terminieren. Wenn ein gültiges Datum erkannt wurde terminiert das Programm mit Exit Code EXIT_SUCCESS. +##### 3.2.1.1 Argument Format +Das Format des Kommando Zeilen Arguments soll yyyy-mm-dd sein, wobei yyyy für das vier-stellige Jahr, mm für einen 1-2-stelligen Monat (1…12) und dd für einen Tag des Monats, begin-nend mit 01. Z.B. 2020-02-29. +##### 3.2.1.2 Korrektes Datum +Das Datum muss alle folgenden Bedingungen erfüllen damit es als korrekt erkannt wird: +* Obergrenze für ein «sinnvolles» Datum ist das Jahr 9999 +* es muss Gregorianisch sein, d.h. ab 15. Oktober 1582 (inklusive) +* es darf nur Monate von 1 für Januar bis 12 für Dezember beinhalten +* der Tag muss grösser oder gleich 1 sein +* der Tag darf nicht grösser als 31 sein für Monate mit einer Länge von 31 Tagen +* der Tag darf nicht grösser als 30 sein für Monate mit einer Länge von 30 Tagen +* der Tag darf für den Februar nicht grösser sein als 29 für ein Schaltjahr +* der Tag darf für den Februar nicht grösser sein als 28 für ein Nicht-Schaltjahr + +##### 3.2.1.3 Vorgaben an die Umsetzung +1. Definieren Sie einen enum Typen mit (typedef) Namen month_t dessen Werte die Englischen 3-Zeichen Abkürzungen der Monate sind, nämlich Jan, Feb, … Dec und stellen Sie sicher dass die Abkürzungen für die uns geläufigen Monatsnummer stehen. +2. Definierend Sie einen struct Typen mit (typedef) Namen date_t und den int Elementen year, month, day. Lesen Sie das Argument (falls vorhanden) via sscanf und dem Formatstring "%d-%d-%d" in die drei Elemente einer Date Variable. Siehe dazu die Hinweise im Anhang. +3. Für die Berechnung der Monatslänge implementieren Sie die Hilfsfunktion is_leap_year(date_t date) (nach obigen Vorgaben). Der Return Wert 0 bedeutet «Kein Schaltjahr», 1 bedeutet «Schaltjahr». +4. Implementieren Sie die Funktion `int get_month_length(date_t date)`. Diese soll für den Monat des Datums die Monatslänge (was dem letzten Tag des Monats ent-spricht) ausgeben – geben Sie 0 für ungültige Monatswerte zurück. +5. Schliesslich implementieren Sie die Funktion int is_gregorian_date(date_t date) welche prüft, ob ein gegebenes Datum im Bereich 15. Oktober 1582 und dem Jahr 9999 ist (0 = nein, 1 = ja). +6. Implementieren Sie eine Funktion int is_valid_date(date_t date), welche obige Bedingungen für ein gültiges Datum umsetzt. Der Return Wert 0 bedeutet «Kein gültiges Datum», 1 bedeutet «Gültiges Datum». Benutzen Sie für die Prüfung des Datums die `month_t` Werte wo immer möglich und sinnvoll. Verwenden Sie die oben implemen-tierten Hilfsfunktionen. +##### 3.2.1.4 Hinweise +Beachten Sie die Kommentare im Code für die geforderten Implementierungs-Details. +#### 3.2.2 Teilaufgabe Wochentag Berechnung +Schreiben Sie eine Funktion welche zu einem Datum den Wochentag berechnet. +Die Formel wird Georg Glaeser zugeschrieben, möglicherweise angelehnt an eine Formel von Carl Friedrich Gauss. + +```{eval-rst} +.. figure:: Wochentagsberechnung.jpg + :width: 600px + :name: Wochentagsberechnung + :align: center +``` +(Quelle: https://de.wikipedia.org/wiki/Wochentagsberechnung) + +Hier ist eine für C abgewandelte Variante davon. +``` +weekday = ((day + (13 * m - 1) / 5 + y + y / 4 + c / 4 - 2 * c) % 7 + 7) % 7 +alle Zahlen sind int Werte und alles basiert auf int-Arithmetik +m = 1 + (month + 9) % 12 +a = year - 1 (für month < Mar), ansonsten year +y = a % 100 +c = a / 100 +``` +Erweitern sie das Programm so, dass vor dem erfolgreichen Terminieren des Programms fol-gende Zeile (inklusive Zeilenumbruch) ausgegeben wird: yyyy-mm-dd is a Ddd, wobei yyyy für das Jahr, mm für die Nummer des Monats (01…12) und dd für den Tag im Monat (01…). Z.B. 2020-02-29 is a Sat. +Vorgaben an die Umsetzung +1. Definieren Sie einen enum Typen mit (typedef) Namen weekday_t dessen Werte die Englischen 3-Zeichen Abkürzungen der Tage sind, nämlich Sun, Mon, … Sat und stel-len Sie sicher dass die Abkürzungen für die Werte 0…6 stehen. +2. Schreiben Sie eine Funktion weekday_t calculate_weekday(date_t date) nach der Beschreibung der obigen Formel. Das date Argument ist als gültig angenom-men, d.h. es ist ein Programmier-Fehler, wenn das Programm diese Funktion mit einem ungültigen Datum aufruft. Machen Sie dafür als erste Codezeile in der Funktion eine Zu-sicherung (assert(is_valid_date(date));) +3. Schreiben Sie eine Funktion void print_weekday(weekday_t day), welche für jeden gülteigen Tag eine Zeile auf stdout schreibt mit den Englischen 3-Zeichen Ab-kürzungen für den Wochentag, z.B. Sonntag: Sun, Montag: Mon, etc. Wenn ein ungülti-ger Wert für day erkannt wird, soll assert(!"day is out-of-range"); aufgeru-fen werden. +Hinweise +• Für interessierte, siehe: https://de.wikipedia.org/wiki/Wochentagsberechnung + + +___ +## 4. Bewertung + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden können. +| Aufgabe | Kriterium | Gewicht | +| :-- | :-- | :-- | +| alle | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| gibIntWert | Eingabe, Bereichsüberprüfung korrekt | 1 | +| istSchaltjahr | Funktion korrekt | 1 | +| TageProMonat | Funktion korrekt | 1 | +| Aufgabe 2 | Fehlenden Teile ergänzt und lauffähig | 1 | +___ +## 5. Anhang + +### 5.1 Sprach Element + +```int main(int argc, char *argv[]) { + ... +} argc: Anzahl Einträge in argv. +argv: Array von Command Line Argumenten. +argv[0]: wie das Programm gestartet wurde +argv[1]: erstes Argument +… +argv[argc-1]: letztes Argument +int a = 0; +int b = 0; +int c = 0; +int res = sscanf(argv[1] + , "%d-%d-%d" + , &a, &b, &c + ); +if (res != 3) { + // Fehler Behandlung... + // ... +} +``` + +### 5.2 Beschreibung +Siehe man 3 sscanf. +Die Funktion sscanf gibt die Anzahl erfolgreich erkannte Argumente zurück. Unbedingt prüfen und angemessen darauf reagieren. +Die gelesenen Werte werden in a, b und c, gespeichert, dazu müssen Sie die Adresse der Variablen übergeben. Mehr Details dazu werden später erklärt. +fprintf(stderr, "Usage: %s…\n", argv[0]); Siehe man 3 fprintf. +Schreibt formatierten Text auf den stderr Stream. + +___ +Version: 15.02.2022 \ No newline at end of file diff --git a/build/html/_sources/P02_Funktionen_Datentyp_enum/README_solution.md.txt b/build/html/_sources/P02_Funktionen_Datentyp_enum/README_solution.md.txt new file mode 100644 index 0000000..5d5cf18 --- /dev/null +++ b/build/html/_sources/P02_Funktionen_Datentyp_enum/README_solution.md.txt @@ -0,0 +1,125 @@ + +# Lösungsskizzen +## Aufgabe 1 +``` +/** + * Tage Pro Monat + * + * Das Programm liest einen Monat (1-12) und ein Jahr (1600-2400) ein und + * gibt die Anzahl der Tage dieses Monats aus. + * + * @author Gerrit Burkert, Adaptation bazz + * @version 15-FEB-2013, 16-OCT-2017, 17-OCT-2019, 16-FEB-2022 + */ + +#include +#include + +#define ERROR_IN_MONTH 1 +#define ERROR_IN_YEAR 2 + +///// Student Code + + + +// Konstante Werte fuer die Monate +// =============================== + +enum { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ }; + + +// Eingabe pruefen (0 ist vom atoi als Fehelcode verwendet und darf nicht verwendet werden) +// =============== + +int gibIntWert(char *name, int von, int bis) { + + int wert; + char wertS[20]; // + + do { + printf("%s: ", name); + fgets(wertS, 20, stdin); + wert = atoi(wertS); + if (wert < von || wert > bis) { + printf("Der Wert muss zwischen %d und %d sein.\n", von, bis); + } else { + break; + } + } while(1); + return wert; +} + + +// Schaltjahr bestimmen +// ==================== + +int istSchaltjahr(int jahr){ + + if ( (jahr % 400 == 0) || ( (jahr %100 != 0) && (jahr % 4 ==0) ) ) + return 1; + else + return 0; +} + + +// Berechnung Anzahl Tage pro Monat +// ================================ + +int tageProMonat(int jahr, int monat) { + + int anzTage; + + // Tage pro Monat bestimmen + switch (monat) { + + // Monate mit 31 Tagen + case JAN: case MAR: case MAI: case JUL: case AUG: case OKT: case DEZ: + anzTage = 31; + break; + + // Monate mit 30 Tagen + case APR: case JUN: case SEP: case NOV: + anzTage = 30; + break; + + // Februar: 28 oder 29 Tage + case FEB: + + if (istSchaltjahr(jahr)) { + anzTage = 29; + } else { + anzTage = 28; + } + break; + } + + return anzTage; +} + +///// END Student Code + + +int main (int argc, char *argv[]) { + + int monat, jahr; + + // Monat einlesen und Bereich ueberpruefen + monat = gibIntWert("Monat", 1, 12); + jahr = gibIntWert("Jahr", 1600, 9999); + + // Ausgabe zum Test + printf("Monat: %d, Jahr: %d \n", monat, jahr); + + // Ausgabe zum Test (hier mit dem ternaeren Operator "?:") + printf("%d ist %s Schaltjahr\n", jahr, istSchaltjahr(jahr) ? "ein" : "kein"); + + // Ausgabe + printf("Der Monat %02d-%d hat %d Tage.\n", monat, jahr, tageProMonat(jahr, monat)); + + return 0; +} + +``` +## Aufgabe 2 +Alter bestehender Boilerplate Code + diff --git a/build/html/_sources/P03_Bit_Operation_struct_typedef/README.md.txt b/build/html/_sources/P03_Bit_Operation_struct_typedef/README.md.txt new file mode 100644 index 0000000..a005934 --- /dev/null +++ b/build/html/_sources/P03_Bit_Operation_struct_typedef/README.md.txt @@ -0,0 +1,193 @@ +# 03 - Bit Operationen, Struct, Typedef + + +## 1. Bit Operationen + +![](./135oALYhkYyXB2aG0F-qrwA.jpeg) + + +Bit Operationen sind allgegenwärtig in den Computer-Wissenschaften und finden in vielen Disziplinen Anwendung. Folgend ein kleiner Auszug aus den wichtigsten Themen: +- **Bit Felder**: Sind die effizienteste Art, etwas darzustellen, dessen Zustand durch mehrere "wahr" oder "falsch" definiert werden kann. Besonders auf Systemen mit begrenzten Ressourcen sollte jede überflüssige Speicher-Allozierung vermieden werden. + + Beispiel: + ```c + // primary colors + #define BLUE 0b100 + #define GREEN 0b010 + #define RED 0b001 + + // mixed colors + #define BLACK 0 /* 000 */ + #define YELLOW (RED | GREEN) /* 011 */ + #define MAGENTA (RED | BLUE) /* 101 */ + #define CYAN (GREEN | BLUE) /* 110 */ + #define WHITE (RED | GREEN | BLUE) /* 111 */ + ``` +[https://de.wikipedia.org/wiki/Bitfeld](https://de.wikipedia.org/wiki/Bitfeld) + +- **Kommunikation**: + - **Prüfsummen/Paritätsbit**: Übertragungsfehler und Integrität können bis zu einem definiertem Grad erkannt werden. Je nach Komplexität der Berechnung können mehrere Fehler erkannt oder auch korrigiert werden. +[https://de.wikipedia.org/wiki/Parit%C3%A4tsbit](https://de.wikipedia.org/wiki/Parit%C3%A4tsbit), [https://de.wikipedia.org/wiki/Pr%C3%BCfsumme](https://de.wikipedia.org/wiki/Pr%C3%BCfsumme) + - **Stoppbit**: Markieren bei asynchronen seriellen Datenübertragungen das Ende bzw. Start eines definierten Blocks. +[https://de.wikipedia.org/wiki/Stoppbit](https://de.wikipedia.org/wiki/Stoppbit) + - **Datenflusssteuerung**: Unterschiedliche Verfahren, mit denen die Datenübertragung von Endgeräten an einem Datennetz, die nicht synchron arbeiten, so gesteuert wird, dass eine möglichst kontinuierliche Datenübermittlung ohne Verluste erfolgen kann. +[https://de.wikipedia.org/wiki/Datenflusssteuerung](https://de.wikipedia.org/wiki/Datenflusssteuerung) + - ... + +- **Datenkompression**: Bei der Datenkompression wird versucht, redundante Informationen zu entfernen. Dazu werden die Daten in eine Darstellung überführt, mit der sich alle – oder zumindest die meisten – Information in kürzerer Form darstellen lassen. +[https://de.wikipedia.org/wiki/Datenkompression](https://de.wikipedia.org/wiki/Datenkompression) +- **Kryptographie**: Konzeption, Definition und Konstruktion von Informationssystemen, die widerstandsfähig gegen Manipulation und unbefugtes Lesen sind. [https://de.wikipedia.org/wiki/Verschl%C3%BCsselung](https://de.wikipedia.org/wiki/Verschl%C3%BCsselung) +- **Grafik-Programmierung**: XOR (oder ^) ist hier besonders interessant, weil eine zweite Eingabe derselben Eingabe die erste rückgängig macht (ein Beispiel dazu weiter unten: "Variablen tauschen, ohne Dritt-Variable +"). Ältere GUIs verwendeten dies für die Hervorhebung von Auswahlen und andere Überlagerungen, um kostspielige Neuzeichnungen zu vermeiden. Sie sind immer noch nützlich in langsamen Grafikprotokollen (z. B. Remote-Desktop). + +### 1.1 Übungen + +#### 1. Basis Operationen +Manipulationen von einzelnen Bits gehören zu den Basis Operationen und dienen als Grundlagen um weitere komplexere Konstrukte zu schaffen. Verfollständigen sie folgendes Beispiel mit den drei Basis Operationen: +```c +#include + +int main() { + unsigned int number; + unsigned int bit = 3; // bit at position 3 + + // Setting a bit + number = ...; // solution: number |= 1 << bit; + + // Clearing a bit + number = ...; // solution: number &= ~(1 << bit); + + // Toggling a bit + number = ...; // solution; number ^= 1 << bit; + + return EXIT_SUCCESS; +} +``` + +#### 2. Variablen tauschen (ohne Dritt-Variable) +Zwei Variablen zu vertauschen scheint ein einfach lösbares Problem zu sein. Eine offensichtliche Variante wäre mittels einer temporären Variablen: +```c +#include +#include + +int main(){ + int a = 3; + int b = 4; + printf("a: %d; b: %d\n", a, b); + + int temp = a; + a = b; + b = temp; + + printf("a: %d; b: %d\n", a, b); + return EXIT_SUCCESS; +} +``` + +Es gibt aber auch eine Variante, die ohne zusätzliche Variable auskommt. Dabei wird die Tatsache, dass eine zweite XOR Operation eine erste XOR Operation rückgängig macht: + +*0011 XOR 0100 = 0111* + +*0111 XOR 0100 = 0011* + +Somit kommt man von einem XOR Resultat (*0111*) wieder auf beide Anfangs Operanden zurück indem man einfach ein zweites Mal mit einem Operanden eine XOR Verknüpfung macht. Damit kann ein Operand als Zwischenspeicher dienen und man muss nicht extra eine Zusatzvariable verwenden. + +Überlegen sie sich wie sie damit zwei Variablen vertauschen können ohne Zusatzvariable: +```c +#include +#include + +int main(){ + int a = 3; + int b = 4; + printf("a: %d; b: %d\n", a, b); + + + ... + + /* Solutions: + // a == 0011; b == 0100 + a ^= b; // a == 0111; b == 0100 + b ^= a; // a == 0111; b == 0011 + a ^= b; // a == 0100; b == 0011 + */ + + printf("a: %d; b: %d\n", a, b); + return EXIT_SUCCESS; +} +``` + +#### 3. Lower- / Uppercase +```c +#include +#include + +int main(){ + char word[8] = "sREedEv"; + char *wordptr = &word[0]; + + while(wordptr < &word[7]) { + printf("UPPERCASE: %c\n", *wordptr & '_'); // converts the char into uppercase regardless of the current casing + printf("LOWERCASE: %c\n", *wordptr | ' '); // converts the char into lowercase regardless of the current casing + wordptr++; + } + + return EXIT_SUCCESS; +} +``` + +#### 4. Prüfen auf 2-er Potenz +```c +#include +#include + +int main(){ + int a=32; + if(a > 0 && (a & (a - 1)) == 0){ + printf("%d is a power of 2", a); + } + return EXIT_SUCCESS; +} +``` +___ +## 2. Struct & typedef + +### 2.1 Übungen + +#### 1. Bit Operationen Rechner + - Bitweise Operationen mit 2 Operanden + - Rechnung wird als ein String über scanf dem Programm übergeben + - String wird in Token zerstückelt und in struct gespeichert: + ```c + typedef struct { + unsigned int operand_1; + unsigned int operand_2; + char operation; + } Expression; + ``` + - Ausgabe in 3 verschiedenen Formaten: + ``` + Bin: + 0000'0000'0000'0001 + & 0000'0000'0000'0011 + ------------------- + 0000'0000'0000'0001 + + Hex + 0x01 & 0x03 = 0x01 + + Dec + 1 & 3 = 1 + ``` + +___ +## 4. Bewertung + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden können. +| Aufgabe | Kriterium | Gewicht | +| :-- | :-- | :-- | +| alle | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| gibIntWert | Eingabe, Bereichsüberprüfung korrekt | 1 | +| istSchaltjahr | Funktion korrekt | 1 | +| TageProMonat | Funktion korrekt | 1 | +| Aufgabe 2 | Fehlenden Teile ergänzt und lauffähig | 1 | diff --git a/build/html/_sources/P04_Modularisieren_von_C_Code/README.md.txt b/build/html/_sources/P04_Modularisieren_von_C_Code/README.md.txt new file mode 100644 index 0000000..e135a32 --- /dev/null +++ b/build/html/_sources/P04_Modularisieren_von_C_Code/README.md.txt @@ -0,0 +1,532 @@ +# 04 - Modularisieren von C Code + +```{eval-rst} +.. figure:: zhaw_neg_P2945.jpg + :width: 100px + :name: logo + :align: right +``` + +___ + +```{eval-rst} +.. figure:: modularisieren_von_c_code.JPG + :width: 500px + :name: logo + :align: center +``` + + +___ + +## Inhalt + +{ref}`04_introduction` + +{ref}`04_learning_objectives` + +{ref}`04_task_01` + +{ref}`04_task_02` + +{ref}`04_grading` + +{ref}`04_appendix` + +___ + +(04_introduction)= +## 1. Übersicht + +In diesem Praktikum üben Sie modulare Programmierung indem Sie ein +Java Programm (bestehend aus drei Java Files) in ein entsprechendes C +Programm aus drei Modulen (aus je einem Header- und Implementations- +File) übersetzen. Sie passen das Makefile so an, dass die +entsprechenden Module mit kompiliert werden. + +In der zweiten Aufgabe erstellen Sie Makefile Regeln für die drei +Schritte von den C Source Files zur graphischen Darstellung der +Abhängigkeiten. + +```{eval-rst} +.. figure:: uebersicht.png + :width: 500px + :name: uebersicht + :align: center +``` + + +Im Anhang ist eine Übersicht über die verwendeten File Formate gegeben. + + +(04_learning_objectives)= +## 2. Lernziele + +In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen. + +* Sie wissen, dass ein Modul aus einem C-File und einem passenden + H-File besteht. +* Sie können Header Files korrekt strukturieren. +* Sie deklarieren im Header-File die öffentlichen Typen und Funktionen + eines Moduls. +* Sie wissen wie **Include Guards** anzuwenden sind. +* Sie können Module im `Makefile` zur Kompilation hinzufügen. +* Sie können `Makefile` Regeln schreiben. + +Die Bewertung dieses Praktikums ist am Ende angegeben. + +Erweitern Sie die vorgegebenen Code Gerüste, welche im `git` +Repository `snp-lab-code` verfügbar sind. + + +(04_task_01)= +## 3. Aufgabe 1: Modularisieren +Das zu ergänzende Programm dep2dot hat folgende Funktionalität: + +Ergänzen Sie in **`modularize/src`** den Code in **`triangle.c`**, +**`read.h`**, **`read.c`**, **`rectang.h`** und **`rectang.c`** so +dass die Tests erfolgreich durchlaufen. Die C Implementation soll +dieselbe Funktionalität haben wie die gegebenen Java Files. Lehnen Sie +sich so nahe wie möglich an die Java Files an. + +1. In den Header-Files implementieren Sie den Include-Guard und + deklarieren Sie die öffentlichen Funktionen und gegebenenfalls + **`#define`**. +2. In den Implementations-Files implementieren Sie die Funktionen. + +Die drei Java Files liegen in **`modularize/java`**. + +### Tipps + +* Implementieren Sie die Symbole welche vollständig in Grossbuchstaben + geschrieben sind als **`#define`**. +* **`EOF`** kommt schon aus **`stdio.h`** und sollte deshalb nicht + mehr definiert werden. +* Jene **`#define`** welche von andern Modulen verwendet werden + kommen ins Header-File, die andern ins Implementations-File. +* Ein Grossteil des Java Codes aus den Methoden Bodies kann + eins-zu-eins in C übernommen werden. Listen Sie auf welche + Unterschiede es gibt: + + + + + + + + + + + + + + + + + + +
JavaC
+ +```Java +byte +``` + +
+ +```Java +boolean +``` + +
+ + +```Java +true +``` +
+ +```Java +false +``` + +
+ +```Java +System.out.print(…) +``` + +
+ +```Java +System.out.println(…) +``` + +
+ +```Java +System.in.read() +``` + + +
+ +```Java +byte[] buffer = new byte[BUFFERSIZE]; +``` + +
+ +```Java +public class rectang { + public boolean Rectangular(…) + { … } +} +``` + +
+ +```Java +public class read { + public int getInt(...) + throws java.io.IOException + { ... } +} +``` + +
+ +```Java +class triangle { + public static void main(String[] args) + throws java.io.IOException + { ... } +} +``` + +
+ +```Java +read ReadInt = new read(); +... +word = ReadInt.getInt(MAX_NUMBER); +``` + +
+ +```Java +rectang Rect = new rectang(); +... +if (Rect.Rectangular(a, b, c) == true) { ... } +``` + +
+ +``` +System.out.println( +"-> Dreieck " + a + "-" + b + "-" + c ++ " ist rechtwinklig"); +``` + +
+ +(04_task_02)= +## 4. Aufgabe 2: Makefile Regeln + +Die folgenden drei Schritte erstellen von einem C Source File eine +graphische Darstellung der Abhängigkeiten: + +1. `gcc ... -H .. file.c ... 2> file.dep` (Regeln im Makefile bereits vorhanden) +2. `dep2dot file.c file.dot` (in dieser Aufgabe zu erstellen) +3. `dot -Tpng file.dot >file.png` (in dieser Aufgabe zu erstellen) + +Sie sollen für die Compiler-ähnlichen Programme `dep2dot` und `dot` +Makefile Regeln schreiben. + +```{eval-rst} +.. figure:: uebersicht.png + :width: 500px + :name: uebersicht + :align: center +``` + + +Das Programm `dep2dot` hat folgende Funktionalität: + + +1. Es liest von `stdin` die vom Compiler generierten + Abhängigkeits-Daten in Form des `dep` Formates ein. +2. Das erste und einzige Command Line Argument gibt das File an für + welches die von `stdin` gelesenen Abhängigkeiten gelten. +3. Auf `stdout` werden die Abhängigkeiten von `stdin` übersetzt als + `dot`-File Format ausgegeben. + +Das Programm `dot` hat folgende Funktionalität: +1. Es liest die textuelle Beschreibung eines Graphen aus der + übergebenen Datei (erstes Argument) ein. +2. Auf `stdout` wird die grafische Darstellung der Beschreibung der + Eingabe-Datei im `png`-File Format ausgegeben. + +Das `dep`-Format und das `dot`-Format sind im Anhang beschrieben. + +Sie können die Funktionalität des Programms `dep2dot` kennen lernen, +indem Sie folgende Zeilen auf der Bash Shell ausführen. Das +`dep.input` File ist Teil der automatisierten Test Suite im +Verzeichnis `tests`: + + +```bash +bin/dep2dot dir/file dep.dot +dot -Tpng dep.dot >dep.png +firefox dep.png +``` + +Als Resultat sollte Firefox folgende Graphik darstellen: + + +```{eval-rst} +.. figure:: dep_dot.png + :width: 150px + :name: dep_dot + :align: center +``` + + + +Definieren Sie im `Makefile` Regeln, welche die einzelnen Schritte von +den Source Files zu den `png` Files ausführen. + + +Prüfen Sie schliesslich die Umsetzung Aufgabe mittels `make dep-clean +dep && firefox src/*.png.` + + +### 4.1 Neue Regeln hinzufügen + + +Führen Sie im `Makefile` an den angegebenen Stellen folgende +Ergänzungen durch + +* definieren Sie eine Variable `DEPFILES` deren Inhalt die Liste alle + Einträge der Variable `SOURCES` ist, wobei bei allen die Endung `.c` + durch die Endung `.c.png` ersetzt ist +* fügen Sie zum `Pseudo-Target .PHONEY` das Target `dep` dazu – dies + besagt, dass das später folgenden Target `dep` nicht ein File + repräsentiert (ohne dieses Setting würde make gegebenenfalls nach + einem File mit Namen `dep` suchen um zu entscheiden ob es + inkrementell gebildet werden muss) +* schreiben Sie das Target `dep` gemäss der Beschreibung im Makefile +* schreiben Sie die Suffix Regel für die Übersetzung von `.png <- + .dot` gemäss Vorgabe im `Makefile` (als Inspiration, siehe auch die + `%.c.dep: %.c` Suffix Regel weiter unten im `Makefile`) – erklären + Sie was die Regel macht +* schreiben Sie die Suffix Regel für die Übersetzung von` .dot <- + .dep` gemäss Vorgabe im `Makefile` – erklären Sie was die Regel + macht + +Die Umsetzung der obigen Änderungen sind erfolgreich, wenn Sie +folgende Shell Command Line erfolgreich ausführen können und in +Firefox die Abhängigkeiten der C-Files von den Inclu-de Files +dargestellt wird. + +`make dep-clean dep && firefox src/*.png.` + + + + +(04_grading)= +## 5. Bewertung + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| 1 | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| 1 | Module einbinden, Header Files schreiben | 2 | +| 2 | Sie können das funktionierende Makefile demonstrieren und erklären. | | +| 2 | Neue Regeln hinzufügen | 2 | + + + + +(04_appendix)= +## 6. Anhang + + + + +### 6.1 Verwendete zusätzliche Sprach Elemente + + + +
+ +**Sprach Element** + + + +**Beschreibung** + +
+ + + +```C +fprintf(stderr, "v=%d", v) +``` + + + +Formatierte Ausgabe auf den Standard Error Stream. Siehe ***man 3 +stderr*** und ***man 3 fprintf***. + +
+ + +### 6.2 Verarbeitung und verwendete File Formate + +Das Programm in diesem Praktikum ist Teil für die graphische +Darstellung von `#include` File Abhängigkeit von C Files. + +Den ersten Schritt für die Darstellung der `#include` File +Abhängigkeiten bietet der Compiler. Der Compiler kann mittels der `-H` +Command Line Option auf `stderr` ein Text File generieren, welches die +tatsächlich verwendeten Header Files auflistet. Zusätzlich wird im +Resultat die Verschachtelungstiefe der Includes angegeben. + +Im zweiten Schritt übersetzt das Programm (`dep2dot`) dieses +Praktikums solche Dependency Files (`dep`) in eine Text Repräsentation +der Abhängigkeiten (`dot`) welche in graphische Darstel-lung (`png`) +übersetzt werden kann. + +Als Tool zur Übersetzung der `dot` Files in das `png` Format dient das +`dot` Tool. Dieses Tool muss gegebenenfalls installiert werden: + +```sudo apt install graphviz``` + +Die `png` Files können dann z.B. in der Programm Dokumentation +integriert werden (Darstellung zu Test Zwecken z.B. mittels `firefox +file.png`). + + +#### 6.2.1 dep File + + +Siehe: `man gcc` + +```bash +-H Print the name of each header file used, in addition to other + normal activities. Each name is indented to show how deep in the + #include stack it is. [...] +``` + +Das File wird auf `stderr` ausgegeben. + +**Beispiel File** (für Abhängigkeiten des `main.c` Files des `dep2dot` Programms) + +```bash +. /usr/include/stdio.h +.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h +... /usr/include/features.h +.... /usr/include/x86_64-linux-gnu/sys/cdefs.h +..... /usr/include/x86_64-linux-gnu/bits/wordsize.h +..... /usr/include/x86_64-linux-gnu/bits/long-double.h +.... /usr/include/x86_64-linux-gnu/gnu/stubs.h +..... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h +.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h +.. /usr/include/x86_64-linux-gnu/bits/types.h +... /usr/include/x86_64-linux-gnu/bits/wordsize.h +... /usr/include/x86_64-linux-gnu/bits/typesizes.h +.. /usr/include/x86_64-linux-gnu/bits/types/__FILE.h +.. /usr/include/x86_64-linux-gnu/bits/types/FILE.h +.. /usr/include/x86_64-linux-gnu/bits/libio.h +... /usr/include/x86_64-linux-gnu/bits/_G_config.h +.... /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h +.... /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h +... /usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h +.. /usr/include/x86_64-linux-gnu/bits/stdio_lim.h +.. /usr/include/x86_64-linux-gnu/bits/sys_errlist.h +. /usr/include/stdlib.h +.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h +.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h +.. /usr/include/x86_64-linux-gnu/bits/floatn.h +... /usr/include/x86_64-linux-gnu/bits/floatn-common.h +.... /usr/include/x86_64-linux-gnu/bits/long-double.h +.. /usr/include/x86_64-linux-gnu/bits/stdlib-float.h +. src/error.h +.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h +. src/data.h +.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h +. src/output.h +Multiple include guards may be useful for: +/usr/include/x86_64-linux-gnu/bits/stdlib-float.h +/usr/include/x86_64-linux-gnu/bits/sys_errlist.h +/usr/include/x86_64-linux-gnu/bits/typesizes.h +/usr/include/x86_64-linux-gnu/gnu/stubs-64.h +/usr/include/x86_64-linux-gnu/gnu/stubs.h +``` + + +#### 6.2.2 dot File + +**Graphviz** ist ein mächtiges Tool-Set welches Graphen, definiert in +einem `dot`-Text File, automatisch anordnet und in `png`, `gif` und +andere Formate übersetzt. + +Siehe die offizielle Web-Page +[https://www.graphviz.org/](https://www.graphviz.org/). + +Es gibt als Teil dieses Tool-Sets verschiedene Übersetzer. Der hier +verwendete ist der Basis-übersetzer: `dot`. + +Das `dot`-File Format kennt viele Möglichkeiten die Knoten und Kanten +eines Graphen und de-ren Anordnung anzugeben. + +Der Vorteil eines solchen Tool-Sets ist, dass man den Inhalt (den +Graphen) einfach definieren kann und sich nicht um das komplexe +Problem der ansprechenden Visualisierung kümmern muss. + +**Beispiel File** (`dot -Tpng sample.dot > sample.png`) + +```C +digraph G { + node [shape=box] + A [label="a.c"]; + B [label="a.h"]; + C [label="b.h"]; + + subgraph cluster_c0 { + label="main"; color=black; + A; + } + + subgraph cluster_c1 { + label="others"; style=filled; col-or=lightgrey; + { B; C; rank=same; } + } + + A -> B; + A -> C; + B -> C; +} +``` + + +#### 6.2.3 png File + +Das `png` Format ist ein verlustfrei komprimiertes Raster Graphik +Format. Es wird oft in Web Pages verwendet. + + +___ +Version: 15.02.2022 \ No newline at end of file diff --git a/build/html/_sources/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.md.txt b/build/html/_sources/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.md.txt new file mode 100644 index 0000000..b78d962 --- /dev/null +++ b/build/html/_sources/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.md.txt @@ -0,0 +1,127 @@ +# 04 - Modularisieren von C Code + +___ +## 1. Übersicht + +In diesem Praktikum wird eine kleine Sammlung von Funktionen als Modul erstellt. + +In der ersten Aufgabe schreiben Sie zu einem bestehenden C Programm die notwendigen Header Files plus passen das Makefile so an, dass die entsprechenden Module mit kompiliert werden. + +In der zweiten Aufgabe erstellen Sie Makefile Regeln um aus Konfigurationsdateien graphischen Darstellungen zu erzeugen. + +___ +## 2. Lernziele + +In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen. + +- Sie wissen, dass ein Modul aus einem C-File und einem passenden H-File bestehen. +- Sie können Header Files korrekt strukturieren. +- Sie wissen wie **Include Guards** anzuwenden sind. +- Sie können Module im **Makefile** zur Kompilation hinzufügen. +- Sie können anhand einer Beschreibung Typen und Funktionen in den passenden Header Files deklarieren. +- Sie können **Makefile** Regeln schreiben. + +Die Bewertung dieses Praktikums ist am Ende angegeben. + +Erweitern Sie die vorgegebenen Code Gerüste, welche im **git** Repository **snp-lab-code** verfügbar sind. + +___ +## 3. Aufgabe 1: Modularisieren + +![](./P04_Aufgabenstellung.png) + +### 3.1 Teilaufgabe Modules einbinden, Header Files schreiben + +- src/objects.h + - 2 Datenstukturen definieren + - `struct point` mit 2 double für x und y Koordinate + - `struct line` mit 2 point +- src/functions.h und .c + - 2 Funktionen deklarieren und definieren + - Berechnung der Länge `get_length`einer Linie (Annahme: Koordinaten sind alle positiv) + - l = sqrt(h^ 2 + b^ 2) + - ev. muss hier in den Anhang `#include ` + - Berechnung der Steigung `get_slope` der Linie gegenüber dem Koordinatensystem + - m = h / b +- tests vorgeben + +- src/objects.h + - Include Guard + - Includes + - Struct für Punkt und Linie + - Include Guard +- src/functions.h + - Include Guard + - Includes + - Deklarationen der Funktionen für Berechnung der Länge und Steigung + - Include Guard +- src/functions.c + - Includes + - Definitionen der Funktionen für Berechnung der Länge und Steigung + - Include Guard + + + + + +___ +## 4. Aufgabe 2: Makefile Regeln + +Makefile ergänzen, damit Modul `functions` korrekt eingebunden und kompiliert wird. + +1. Kompilieren Sie das ganze mittels **make clean default**. Es sollten keine Compiler Fehler auftreten. + +### 4.1 Neue Regeln hinzufügen + +- Vorraussetzung: tab2svg.sh aus Praktikum 3 wird um die Möglichkeit erweitert eine Linie zu zeichnen (`line:x1:y1:x2:y2:color`) +- Studierende erstellen + - mind. 2 Files `long.line` und `short.line` mit 2 unterschiedlichen Linien + - Makefile Regeln um aus einem File `.line` ein File `.svg` mit Hilfe des Scripts zu erstellen + - PHONY Regel `display` um beide `.svg` mit Firefox darzustellen + - Vorgabe: sie sollen eine Variable für die Input-Dateien nutzen + +Nachdem das Programm in Aufgabe 1 umgesetzt ist, geht es nun darum, im **Makefile** Regeln zu definieren welche die einzelnen Schritte von den Source Files zu den **png** Files ausführen. + +Prüfen Sie schliesslich die Umsetzung mittels `make display`. + +___ +## 5. Aufgabe 3 +- Studierende sollen Ausgabe von `make doc` analysieren und die Include Diagramme erklären können +``` +make doc +firefox doc/index.html & +``` + +___ +## 6. Bewertung + + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +___ +## 7. Erweiterung Doxyfile für Abhängigkeitsanalyse + +``` +--- /home/vagrant/huno/snp-new/snp/praktika/Shared/work/Doxyfile 2022-02-07 21:16:42.343302707 +0100 ++++ /home/vagrant/snp/Doxyfile 2022-02-07 22:22:36.266839126 +0100 +@@ -297,14 +297,14 @@ + UML_LOOK = NO + UML_LIMIT_NUM_FIELDS = 10 + TEMPLATE_RELATIONS = NO +-INCLUDE_GRAPH = NO +-INCLUDED_BY_GRAPH = NO ++INCLUDE_GRAPH = YES ++INCLUDED_BY_GRAPH = YES + CALL_GRAPH = NO + CALLER_GRAPH = NO +-GRAPHICAL_HIERARCHY = NO +-DIRECTORY_GRAPH = NO ++GRAPHICAL_HIERARCHY = YES ++DIRECTORY_GRAPH = YES + DOT_IMAGE_FORMAT = png +-INTERACTIVE_SVG = NO ++INTERACTIVE_SVG = YES + DOT_PATH = + DOTFILE_DIRS = + MSCFILE_DIRS = +``` diff --git a/build/html/_sources/P05_TicTacToe/P05_TicTacToe.rst.txt b/build/html/_sources/P05_TicTacToe/P05_TicTacToe.rst.txt new file mode 100644 index 0000000..58512e9 --- /dev/null +++ b/build/html/_sources/P05_TicTacToe/P05_TicTacToe.rst.txt @@ -0,0 +1,272 @@ +05 - SNP: TicTacToe +############## + + + +.. image:: zhaw_neg_P2945.jpg + :width: 100px + :height: 100px + :scale: 25 % + :align: right + +.. contents:: + + + +Übersicht +********* +In diesem Praktikum erweitern Sie einen Programm Rahmen zu einem funktionierenden TicTacToe Spiel. Bei TicTacToe legen zwei Spieler abwechselnd auf einem 3x3 Brett einen Stein, bis ein Spieler mit einer horizontalen, vertikalen oder diagonalen Linie gewinnt, oder alle Felder besetzt sind. + +.. image:: TicTacToe.svg + +In der Aufgabe implementieren Sie die fehlenden Funktionen bis alle Tests erfolgreich durchlau-fen. Die gewählte Vorgehensweise ist somit TDD – Test-Driven-Development: es existieren zuerst Tests welche alle fehlschlagen. Schrittweise werden die Funktionen implementiert bis alle Tests erfolgreich durchlaufen. + +Wenn die Tests erfolgreich durchlaufen, wird auch das Programm funktionieren und Sie können jemanden mit dem Spiel herausfordern 😉. + + + +Lernziele +********* +In diesem Praktikum lernen Sie den Zugriff auf Arrays. + + * Sie können anhand einer Beschreibung im Code die fehlenden Funktionen implementieren wo auf Arrays zugegriffen wird. + +Die Bewertung dieses Praktikums ist am Ende angegeben. + +Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind. + + +Aufgabe: TicTacToe +****************** +Das zu ergänzende Programm tic-tac-toe hat folgende Funktionalität: +#. es stellt ein 3x3 TicTacToe Spielbrett auf dem Terminal dar +#. es liest von stdin eine Ziffer 0…9 ein, wobei 0 für Programm-Terminieren, die übrigen Ziffern für die Wahl eines Feldes stehen +#. der erste Spielzug wird von Spieler A geführt, danach wechselt das Programm zwischen den Spielern A und B +# bei Gewinn oder bei vollem Brett ist das Spiel vorbei + +Wenn die Aufgabe erfolgreich umgesetzt ist, können Sie das Spiel ausführen: +```bash +bin/tic-tac-toe +``` + +Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (``make test``). + +Die Architektur des Programms folgt dem MVC – Model-View-Control Paradigma. Dieses Paradigma besagt, dass die View (Eingabe und Darstellung) über Control (Vermittler) das Modell (die eigentliche Programm-Logik) steuert und darstellt. Dabei sind folgende Abhängigkeiten gegeben: + +.. image:: MVC_pattern.svg + + + +Test-Driven-Development +======================= + +Das Programm besteht aus folgenden Files: + +.. list-table:: + + * - Datei + - ToDo + * - Makefile + - - + * - tests/tests.c + - - + * - src/main.c + - - + * - src/view.h + - - + * - src/view.c + - - + * - src/control.h + - - + * - src/control.c + - - + * - src/model.h + - - + * - src/model.c + - siehe unten + + +1. Führen Sie ``make test`` aus:: + + Suite: lab test + Test: test_model_init + init_model:... 0/0 FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_get_state ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_get_winner ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_can_move ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_move ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_get_win_line ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + + Run Summary: Type Total Ran Passed Failed Inactive + suites 1 1 n/a 0 0 + tests 6 6 0 6 0 + asserts 6 6 0 6 n/a + + + +2. Konzentrieren Sie sich auf den ersten Test der fehlschlägt. Dies ist ein Unit Test, welcher die Funktion **model_init()** prüft. Suchen Sie die Funktion in **src/model.h** und **src/model.c**. +Was ist die geforderte Funktionalität und wie ist sie implementiert? +Suchen Sie die darin aufgerufene **model_init()** Funktion und implementieren Sie diese. + +.. code-block:: c + + void model_init(model_t *instance) { + assert(instance); + // Instructions to the students: + // set all fields of the board to model_state_none + // BEGIN-STUDENTS-TO-ADD-CODE + + // END-STUDENTS-TO-ADD-CODE } + + + + +3. Führen Sie ``make test`` und korrigieren Sie obige Funktion, bis der Test nicht mehr fehlschlägt. + + + +3.2 test_model_init +=================== + +Gehen Sie analog zur ersten Teilaufgabe vor: +1. Führen Sie ``make test`` aus. +2. Suchen Sie die Funktion **model_get_state()** in **model.h** und **model.c**. +3. Implementieren Sie die intern benutzte Funktion **get_state()** gemäss der Anleitung im Code. + +.. code-block:: c + + model_state_t model_get_state(model_t *instance, model_pos_t pos) + { + assert(instance); + assert_pos(pos); + + // Instructions to the students: + // replace the stub implementation my access to the field at the given position. + // BEGIN-STUDENTS-TO-ADD-CODE + + + return model_state_none; // stub + + + // END-STUDENTS-TO-ADD-CODE + } + +4. Führen Sie ``make test`` und korrigieren Sie, bis die beiden Tests nicht mehr fehlschlagen. + + + +3.3 test_model_get_state test_model_get_winner +============================================== + +Gehen Sie analog zur ersten Teilaufgabe vor: +1. Führen Sie ``make test`` aus. +2. Suchen Sie die Funktion **model_get_state()** in **model.h** und **model.c**. +3. Implementieren Sie die intern benutzte Funktion **get_state()** gemäss der Anleitung im Code. + +.. code-block:: c + + model_state_t model_get_state(model_t *instance, model_pos_t pos) + { + assert(instance); + assert_pos(pos); + + // Instructions to the students: + // replace the stub implementation my access to the field at the given position. + // BEGIN-STUDENTS-TO-ADD-CODE + + + return model_state_none; // stub + + + // END-STUDENTS-TO-ADD-CODE + } + + + + + +3.4 test_model_can_move +======================= + +Gehen Sie analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **model_can_move()**. + +.. code-block:: c + + int model_can_move(model_t *instance){ + assert(instance); + if (model_get_winner(instance) == model_state_none) { + // Instructions to the students: + // scan all fields: return 1 with first field which equals model_state_none + // BEGIN-STUDENTS-TO-ADD-CODE + + // END-STUDENTS-TO-ADD-CODE} + return 0;} + + + + +3.5 test_model_move test_model_get_win_line +=========================================== + +Schliesslich gehen Sie auch hier analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **set_state()**. + +.. code-block:: c + + /** + * @brief Sets the field on the board to the given state. + * @param instance [INOUT] The instance which holds the state. + * @param pos [IN] The affected field. + * @param state [IN] The new state of the field. + */ + static void set_state(model_t *instance, model_pos_t pos, model_state_t state) + { + assert_pos(pos); + + // Instructions to the students: + // set the field of the board to the new state + // BEGIN-STUDENTS-TO-ADD-CODE + + // END-STUDENTS-TO-ADD-CODE + } + + + +Wenn die beiden obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch und das Spiel kann gespielt werden. + + +4. Bewertung +****************** + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + + +.. list-table:: + :header-rows: 0 + + * - Aufgabe + - Kriterium + - Gewicht + * - TicTacToe + - Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. + - + * - TicTacToe + - Teilaufgabe test_model_init + - 1 + * - TicTacToe + - Teilaufgabe test_model_get_state und test_model_get_winner + - 1 + * - TicTacToe + - Teilaufgabe test_model_can_move + - 1 + * - TicTacToe + - Teilaufgabe test_model_move und test_model_get_win_line + - 1 + + +Version: 15.02.2022 diff --git a/build/html/_sources/P05_TicTacToe/README.md.txt b/build/html/_sources/P05_TicTacToe/README.md.txt new file mode 100644 index 0000000..02d33cf --- /dev/null +++ b/build/html/_sources/P05_TicTacToe/README.md.txt @@ -0,0 +1,205 @@ +# 05 - Arrays/Strings/TicTacToe +___ + +## 1. Übersicht + In diesem Praktikum werden Sie in der ersten Aufgabe ein Programm zum Einlesen, Sortieren und Ausgeben von Strings von Grund auf entwickeln. + + In der zweiten Aufgabe werden Sie einen Programmrahmen zu einem funktionierenden TicTacToe-Spiel erweitern. Sie implementieren hierbei die fehlenden Funktionen bis alle Tests erfolgreich durchlaufen. Die gewählte Vorgehensweise entspricht somit Test-Driven-Development (TDD). D.h. es existieren zuerst Tests, welche alle fehlschlagen. Schrittweise werden die Funktionen implementiert bis alle Tests erfolgreich durchlaufen. Wenn die Tests erfolgreich durchlaufen, wird auch das Programm funktionieren. + +___ + +## 2. Lernziele +In diesem Praktikum schreiben Sie selbst von Grund auf ein C-Programme, das mit Strings operiert. Ferner ergänzen Sie ein bestehendes und lernen dabei den Zugriff auf Arrays. + + * Sie können mit Arrays von Strings umgehen. + * Sie können String-Funktionen aus der Standard Library verwenden. + * Sie können anhand einer Beschreibung im Code die fehlenden Funktionen die auf Arrays zugreifen implementieren. + +___ + +## 3. Aufgabe 1: Sortieren von Strings +Schreiben Sie ein C-Programm, das bis zu 10 Wörter mit einer maximalen Länge von jeweils 20 char von der Tastatur einliest, diese in Grossbuchstaben umwandelt, in einem Array der Reihe nach ablegt und zum Schluss im Array alphabetisch sortiert und ausgibt. Wiederholt eingegebene Wörter sollen dabei ignoriert werden. Das Ende der Eingabe soll durch das Erreichen der zehn unterschiedlichen Wörter oder durch die Eingabe von „ZZZ“ erfolgen. Die Ausgabe der sortierten Wörter soll direkt nach Beendigung der Eingabe erfolgen. + +Hinweise: +- Zur Speicherung der Wörter sollten Sie ein zweidimensionales Array verwenden. +- Verwenden Sie die String-Funktionen der C Standard Library (include ), z.B. um Strings alphabetisch zu vergleichen. +- Wenn Sie aus anderen Vorlesungen bereits einen effizienten Sortieralgorithmus kennen, können Sie diesen natürlich verwenden. Sonst erfinden Sie einfach einen eigenen. +- Strukturieren Sie das Programm durch geeignete Funktionen. + +___ + +## 4. Aufgabe 2: TicTacToe +Das zu ergänzende Programm tic-tac-toe hat folgende Funktionalität: +1. es stellt ein 3x3 TicTacToe Spielbrett auf dem Terminal dar +2. es liest von stdin eine Ziffer 0…9 ein, wobei 0 für Programm-Terminieren, die übrigen Ziffern für die Wahl eines Feldes stehen +3. der erste Spielzug wird von Spieler A geführt, danach wechselt das Programm zwischen den Spielern A und B +4. bei Gewinn oder bei vollem Brett ist das Spiel vorbei + +Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind. + +Wenn die Aufgabe erfolgreich umgesetzt ist, können Sie das Spiel ausführen: +```bash +bin/tic-tac-toe +``` + + +![](./TicTacToe.png) + +Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (`make test`). + +Die Architektur des Programms folgt dem MVC – Model-View-Control Paradigma. Dieses Paradigma besagt, dass die View (Eingabe und Darstellung) über Control (Vermittler) das Modell (die eigentliche Programm-Logik) steuert und darstellt. Dabei sind folgende Abhängigkeiten gegeben: + + +![](./MVC_pattern.png) +___ + +### 4.1 Teilaufgabe test_model_init +Das Programm besteht aus folgenden Files: +| Datei | ToDo | +| :-- | :-- | +|Makefile | -> gegeben, d.h. nichts anzupassen | +|tests/tests.c| -> gegeben, d.h. nichts anzupassen | +|src/main.c| -> gegeben, d.h. nichts anzupassen | +|src/view.h| -> gegeben, d.h. nichts anzupassen | +|src/view.c| -> gegeben, d.h. nichts anzupassen | +|src/control.h| -> gegeben, d.h. nichts anzupassen | +|src/control.c| -> gegeben, d.h. nichts anzupassen | +|src/model.h| -> gegeben, d.h. nichts anzupassen | +|src/model.c| -> **anzupassen:** umsetzen gemäss den Angaben unten | + +1. Führen Sie `make test` aus +```bash +Suite: lab test + Test: test_model_init ... + init_model:... 0/0 FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_get_state ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_get_winner ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_can_move ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_move ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + Test: test_model_get_win_line ...FAILED + 1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none) + +Run Summary: Type Total Ran Passed Failed Inactive + suites 1 1 n/a 0 0 + tests 6 6 0 6 0 + asserts 6 6 0 6 n/a + +``` +2. Konzentrieren Sie sich auf den ersten Test der fehlschlägt. Dies ist ein Unit Test, welcher die Funktion **model_init()** prüft. Suchen Sie die Funktion in **src/model.h** und **src/model.c**. +3. Was ist die geforderte Funktionalität und wie ist sie implementiert? + +Suchen Sie die darin aufgerufene **model_init()** Funktion und implementieren Sie diese. +```c +void model_init(model_t *instance) +{ + assert(instance); + + // Instructions to the students: + // set all fields of the board to model_state_none + // BEGIN-STUDENTS-TO-ADD-CODE + + + + // END-STUDENTS-TO-ADD-CODE +} + +``` +3. Führen Sie `make test` und korrigieren Sie obige Funktion, bis der Test nicht mehr fehlschlägt. + +___ + +### 4.2 Teilaufgabe test_model_get_state und test_model_get_winner +Gehen Sie analog zur ersten Teilaufgabe vor: +1. Führen Sie `make test` aus. +2. Suchen Sie die Funktion **model_get_state()** in **model.h** und **model.c**. +3. Implementieren Sie die intern benutzte Funktion **get_state()** gemäss der Anleitung im Code. + +```c +model_state_t model_get_state(model_t *instance, model_pos_t pos) +{ + assert(instance); + assert_pos(pos); + + // Instructions to the students: + // replace the stub implementation my access to the field at the given position. + // BEGIN-STUDENTS-TO-ADD-CODE + + + return model_state_none; // stub + + + // END-STUDENTS-TO-ADD-CODE +} + + +``` +___ + +### 4.3 Teilaufgabe test_model_can_move +Gehen Sie analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **model_can_move()**. +```c +int model_can_move(model_t *instance) +{ + assert(instance); + if (model_get_winner(instance) == model_state_none) { + // Instructions to the students: + // scan all fields: return 1 with first field which equals model_state_none + // BEGIN-STUDENTS-TO-ADD-CODE + + + + // END-STUDENTS-TO-ADD-CODE + } + return 0; +} + +``` +___ + +### 4.4 Teilaufgabe test_model_move und test_model_get_win_line +Schliesslich gehen Sie auch hier analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **set_state()**. +```c +/** + * @brief Sets the field on the board to the given state. + * @param instance [INOUT] The instance which holds the state. + * @param pos [IN] The affected field. + * @param state [IN] The new state of the field. + */ +static void set_state(model_t *instance, model_pos_t pos, model_state_t state) +{ + assert_pos(pos); + + // Instructions to the students: + // set the field of the board to the new state + // BEGIN-STUDENTS-TO-ADD-CODE + + + + + // END-STUDENTS-TO-ADD-CODE +} + +``` + +Wenn die beiden obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch und das Spiel kann gespielt werden. +___ + +## 5. Bewertung + +Der funktionierende Programmcode muss der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| Sortieren von Strings | Sie können das funktionierende Programm demonstrieren und erklären. | 2 | +| TicTacToe | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| TicTacToe | Teilaufgabe test_model_init | 0.5 | +| TicTacToe | Teilaufgabe test_model_get_state und test_model_get_winner | 0.5 | +| TicTacToe | Teilaufgabe test_model_can_move | 0.5 | +| TicTacToe | Teilaufgabe test_model_move und test_model_get_win_line | 0.5 | +___ +Version: 14.02.2022 diff --git a/build/html/_sources/P06_Personen_Verwaltung_Linked_List/README.md.txt b/build/html/_sources/P06_Personen_Verwaltung_Linked_List/README.md.txt new file mode 100644 index 0000000..a45e70b --- /dev/null +++ b/build/html/_sources/P06_Personen_Verwaltung_Linked_List/README.md.txt @@ -0,0 +1,254 @@ +# 06 - Personen Verwaltung – Linked List +___ + +![](./linked_list.png) + +___ + +## 1. Übersicht +In diesem Praktikum schreiben Sie eine einfache Personenverwaltung. Dabei werden Sie etliche Elemente von C anwenden: +* Header Files selber schreiben, inklusive Include Guard +* Typen definieren +* Funktionen mit `by value` und `by reference` Parametern deklarieren und definieren +* einfache Variablen, Pointer Variablen, struct Variablen und Array Variablen benutzen +* Strukturen im Speicher dynamisch allozieren und freigeben +* I/O und String Funktionen aus der Standard Library anwenden +* Anwender Eingaben verarbeiten +* Fehlerbehandlung + +___ + +## 2. Lernziele + +In diesem Praktikum wenden Sie viele der bisher gelernten C Elemente an. +* Sie können anhand dieser Beschreibung ein vollständiges C Programm schreiben. +* Sie können Unit Tests schreiben welche die wesentlichen Funktionen des Programms individuell testen. +* +Die Bewertung dieses Praktikums ist am Ende angegeben. + +Erweitern Sie die vorgegebenen Code Gerüste, welche im `git` Repository `snp-lab-code` verfügbar sind. + + +___ + +## 3. Personenverwaltung +___ + + +### 3.1 Programmfunktion +Das Programm soll in einer Schleife dem Benutzer jeweils folgende Auswahl bieten, wovon eine Aktion mit Eingabe des entsprechenden Buchstabens ausgelöst wird: + +**I**(nsert), **R**(emove), **S**(how), **C**(lear), **E**(nd): +* **Insert**: der Benutzer wird aufgefordert, eine Person einzugeben +* **Remove**: der Benutzer wird aufgefordert, die Daten einer zu löschenden Person einzu-geben +* **Show**: eine komplette Liste aller gespeicherten Personen wird in alphabetischer Rei-henfolge ausgegeben +* **Clear**: alle Personen werden gelöscht +* **End**: das Programm wird beendet + +___ + +### 3.2 Designvorgaben + +**Verkettete Liste** +Da zur Kompilierzeit nicht bekannt ist, ob 10 oder 10'000 Personen eingegeben werden, wäre es keine gute Idee, im Programm einen statischen Array mit z.B. 10'000 Personen-Einträgen zu allozieren. Dies wäre ineffizient und umständlich beim sortierten Einfügen von Personen. In solchen Situationen arbeitet man deshalb mit dynamischen Datenstrukturen, die zur Laufzeit beliebig (solange Speicher vorhanden ist) wachsen und wieder schrumpfen können. Eine sehr populäre dynamische Datenstruktur ist die **verkettete Liste** und genau die werden wir in diesem Praktikum verwenden. + + + +![](./a.png) + +![](./b.png) + +![](./c.png) + +![](./d.png) +Abbildung 1: Zyklisch verkettete Liste + + +Eine verkettete Liste bedeutet, dass ein Knoten der verketten Liste einen Datensatz einer Person speichert und zusätzlich einen Pointer auf den nächsten Knoten in der Liste aufweist (siehe Abbildung 1). In dieser Pointer Variablen (`next` in der `node_t` Struktur unten) steht also einfach die Adresse des nächsten Knotens. + +Die leere Liste besteht aus einem einzelnen Element, welches keine spezifische Person abspeichert und welches auf sich selbst zeigt (Abbildung 1 a). Dieses Element ist der Einstiegspunkt der Liste (auch Anker oder Wurzel genannt) und ist das einzige Element, das Sie im Programm direkt kennen und einer Variablen zuweisen. Dieses Element können Sie statisch allozieren (z.B. `node_t anchor`;, siehe Details weiter unten), denn es existiert während der gesamten Ausführungszeit. Alle anderen Elemente erreichen Sie ausgehend vom Anker, indem Sie einmal, den Pointern folgend, im Kreis herum gehen. Abbildung 1 b zeigt die Liste nach dem Einfügen der Person `Max Mueller, 40` Jahre. Nach dem Einfügen von zwei weiteren Personen sieht die Datenstruktur aus wie in Abbildung 1 c. Das Entfernen der Person `Arno Bosshard` führt zu Abbildung 1 d. + +Eine Person kann **zugefügt** werden, indem dynamisch ein neuer Knoten erzeugt wird und dieser in die verkettete Liste eingefügt wird. Beim Einfügen müssen die Adressen der Knoten so den Pointern zugewiesen werden, dass die Kette intakt bleibt. + +Ein Knoten wird **entfernt**, indem der entsprechende Knoten aus der Verkettung herausgelöst wird (`next` des Vorgängerknotens soll neu auf `next` des herauszulösenden Knotens zeigen) und dann der Speicher des entsprechenden Knotens freigegeben wird. + +**Personen und Knoten Records** + +Die für je eine Person zu speichernden Daten sollen in folgendem C `struct` zusammengefasst sein. + +```C +#define NAME_LEN 20 + +typedef struct { + char name[NAME_LEN]; + char first_name[NAME_LEN]; + unsigned int age; +} person_t; +``` + +Jeder Knoten der verketteten Liste soll aus folgendem C `struct` bestehen. + +```C +typedef struct node { + person_t content; // in diesem Knoten gespeicherte Person + struct node *next; // Pointer auf den nächsten Knoten in der Liste +} node_t; +``` + +**Vorschlag: zyklisch verkettete Liste** + +Erkennen des Endes der Liste: bei der zyklisch verketteten Liste zeigt das letzte Element wie-der auf den Anker, die Liste bildet also einen Kreis. Dies ist in Abbildung 1 so abgebildet. + +Alternativ könnte man das Ende erkennbar machen, indem die Kette anstelle von zyklisch, mit einem NULL Pointer endet. + +Die Wahl ist ihnen überlassen ob sie die eine oder andere Art der End-Erkennung implementieren. In der Beschreibung wird angenommen, dass es sich um eine zyklisch verkettete Liste handelt. + +**Sortiertes Einfügen** + +Die Personen Records sollen sortiert in die Liste eingefügt werden. Dies bedeutet, dass vom Anker her gesucht werden soll, bis der erste Knoten gefunden wurde dessen Nachfolgeknoten entweder „grösser“ ist als der einzufügende Knoten, oder wo das Ende der Liste erreicht ist. Die Ordnung (grösser, gleich, kleiner) soll so definiert sein: + +```C +// if (p1 > p2) { ... } +if (person_compare(&p1, &p2) > 0) { ... } +/** + * @brief Compares two persons in this sequence: 1st=name, 2nd=first_name, 3rd=age + * @param a [IN] const reference to 1st person in the comparison + * @param b [IN] const reference to 2nd person in the comparison + * @return =0 if all record fields are the same + * >0 if all previous fields are the same, but for this field, a is greater + * <0 if all previous fields are the same, but for this field, b is greater + * @remark strncmp() is used for producing the result of string field comparisons + * @remark a->age – b->age is used for producing the result of age comparison + */ +int person_compare(const person_t *a, const person_t *b); +``` + +**Eingabe** + +**Fehlerhafte Wahl der Operation** in der Hauptschleife soll gemeldet werden, ansonsten aber ignoriert werden. + +**Fehlerhafte Eingabe der Personenangaben** sollen gemeldet werden und die gesamte Operation (z.B. Insert) verworfen werden. + +Zu prüfende Fehler bei Personeneingaben: +* für die Namen + * zu lange Namen +* für das Alter + * keine Zahl +* Duplikat + * derselbe Record soll nicht doppelt in der Liste vorkommen + +Weitergehende Prüfungen sind nicht erwartet. + +**Zu beachten:** bei fehlerhafter Eingabe darf kein „Memory Leak“ entstehen, d.h. potentiell auf dem Heap allozierter Speicher muss im Fehlerfall freigegeben werden. + + +___ + +### 3.3 Bestehender Programmrahmen + +Der Programmrahmen besteht aus den unten aufgelisteten Files. Es sollen weitere Module in `src` hinzugefügt werden und die bestehenden Files ergänzt werden gemäss den Aufgaben. + +| | | +| :-- | :-- | +| Makefile | -> **zu ergänzen** mit neuen Modulen | +| tests/tests.c | -> **zu ergänzen** gemäss Aufgaben (implementieren von Unit Tests) | +| src/main.c | -> **zu ergänzen** gemäss Aufgaben (Hauptprogramm) | + +___ + +## 4. Aufgabe 1: Modularisierung – API und Implementation main.c +Kreieren Sie folgende Files in `src` und implementieren Sie `main.c` basierend auf dem unten von Ihnen gegebenen API. + + +**File person.h** + + +Typ Definitionen: +```C +person_t... // siehe Beschreibung oben +``` + +Funktionsdeklarationen: +```C +// siehe Beschreibung oben +int person_compare(const person_t *a, const person_t *b); +``` + +* gegebenenfalls weitere Funktionen für die Bearbeitung von Personen + + + + +**File list.h** + +Typ Definitionen: +```C +person_t... // siehe Beschreibung oben +``` + +Funktionsdeklarationen: +* Funktionen für `insert`, `remove`, `clear` Operationen auf der Liste + +___ + +Das Hauptprogramm soll die Eingabeschleife implementieren und die obigen Funktionen (wo angebracht) aufrufen. + +___ + +## 5. Aufgabe 2: Implementierung von person.c und list.c + +Fügen Sie die beiden Implementationsfiles `person.c` und `list.c` zu `src`. Fügen Sie die beiden Module im `Makefile` zu der vorgegebenen Variablen `MODULES` hinzu, so dass sie beim `make` Aufruf auch berücksichtigt werden. + +___ + +### 5.1 Teilaufgabe: Implementierung von person.c +Implementieren Sie die Funktionen aus `person.h`. + +Falls nötig, stellen Sie weitere statische Hilfsfunktionen in `person.c` zur Verfügung. + +___ + +### 5.2 Teilaufgabe: Implementierung von list.c + +Implementieren Sie die Funktionen aus `list.h`. + +Falls nötig, stellen Sie weitere statische Hilfsfunktionen in `list.c` zur Verfügung. + +___ + +## 6. Aufgabe 3: Unit Tests + +Schreiben Sie Unit Tests für mindestens die folgenden Funktionen + +* `person.h:` + * `person_compare` +* `list.h:` + * `list_insert` + * `list_remove` + * `list_clear` + +Es existieren in `tests/tests.c` schon vier Test Rahmen für diese Test Cases. + +In diese Test Cases sollen die entsprechenden Funktionen unter verschiedenen Bedingungen isoliert aufgerufen werden und deren Verhalten überprüft werden. + +Verwenden Sie für die Überprüfung die CUnit `CU_ASSERT_...` Makros. + +Siehe dazu auch `man CUnit`. + +Wenn die obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch. + + +___ + +## 7. Bewertung + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| 1 | API von list.h und person.h plus die Implementation von main.c | 2 | +| 2 | Teilaufgabe: person.c | 2 | +| 2 | Teilaufgabe: list.c | 2 | +| 3 | Unit Tests | 2 | + +___ +Version: 11.01.2022 diff --git a/build/html/_sources/P07_Prozesse_und_Threads/README.md.txt b/build/html/_sources/P07_Prozesse_und_Threads/README.md.txt new file mode 100644 index 0000000..bdbd3bd --- /dev/null +++ b/build/html/_sources/P07_Prozesse_und_Threads/README.md.txt @@ -0,0 +1,441 @@ +# 07 - Prozesse und Threads +___ + +![](./ein_mann_orchester.png) + + +[Quelle: https://www.wikiwand.com/de/Ein-Mann-Orchester](https://www.wikiwand.com/de/Ein-Mann-Orchester) + +___ + +## 1. Übersicht +In diesem Praktikum werden wir uns mit Prozessen, Prozesshierarchien und Threads beschäftigen, um ein gutes Grundverständnis dieser Abstraktionen zu erhalten. Sie werden bestehenden Code analysieren und damit experimentieren. D.h. dies ist nicht ein «Codierungs»-Praktikum, sondern ein «Analyse»- und «Experimentier»-Praktikum. +___ + +### 1.1 Nachweis +Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen. + +Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman. + + +___ + +## 2. Lernziele +In diesem Praktikum werden Sie sich mit Prozessen, Prozesshierarchien und Threads beschäftigen. Sie erhalten einen vertieften Einblick und Verständnis zur Erzeugung, Steuerung und Terminierung von Prozessen unter Unix/Linux und Sie werden die unterschiedlichen Eigenschaften von Prozessen und Threads kennenlernen. +* Sie können Prozesse erzeugen und die Prozesshierarchie erklären +* Sie wissen was beim Erzeugen eines Prozesses vom Elternprozess vererbt wird +* Sie wissen wie man auf die Terminierung von Kindprozessen wartet +* Sie kennen die Unterschiede zwischen Prozessen und Threads +___ + +## 3. Aufgaben +Das Betriebssystem bietet Programme um die aktuellen Prozesse und Threads darzustellen. + +Die Werkzeuge kommen mit einer Vielzahl von Optionen für die Auswahl und Darstellung der Daten, z.B. ob nur Prozesse oder auch Threads aufgelistet werden sollen, und ob alle Prozesse oder nur die «eigenen» Prozesse ausgewählt werden sollen, etc. + +Siehe die entsprechenden `man` Pages für weitere Details. + +Eine Auswahl, welche unter Umständen für die folgenden Aufgaben nützlich sind: + +| | | +| :-- | :-- | +| `ps` | Auflisten der Prozess Zustände zum gegebenen Zeitpunkt. | +| `pstree` | Darstellung der gesamten Prozesshierarchie. | +| `top` | Wie `ps`, aber die Darstellung wird in Zeitintervallen aufdatiert. | +| `htop` | Wie `top`, aber zusätzlich dazu die Auslastung der CPU in einem System mit mehreren CPUs. | +| `lscpu` | Auflisten der CPUs. | +| `cat`/`proc`/`cpuinfo` | Ähnlich zu `lscpu`, aber mit Zusatzinformationen wie enthaltene CPU Bugs (z.B. `bugs: cpu_meltdown spectre_v1 spect-re_v2 spec_store_bypass l1tf mds swapgs itlb_multihit`) | + +___ + +### 3.1 Aufgabe 1: Prozess mit fork() erzeugen + +**Ziele** + +* Verstehen, wie mit `fork()` Prozesse erzeugt werden. +* Einfache Prozesshierarchien kennenlernen. +* Verstehen, wie ein Programm, das `fork()` aufruft, durchlaufen wird. + +**Aufgaben** +1. Studieren Sie zuerst das Programm `ProcA1.c` und beschrieben Sie was geschieht. + ``` + + + ``` +2. Notieren Sie sich, was ausgegeben wird. Starten Sie das Programm und vergleichen Sie die Ausgabe mit ihren Notizen? Was ist gleich, was anders und wieso? + ``` + + + ``` + +___ + +### 3.2 Aufgabe 2: Prozess mit fork() und exec(): Programm Image ersetzen + +**Ziele** +* An einem Beispiel die Funktion `execl()` kennenlernen. +* Verstehen, wie nach `fork()` ein neues Programm gestartet wird. +**Aufgaben** +1. Studieren Sie zuerst die Programme `ProcA2.c` und `ChildProcA2.c`. +2. Starten Sie `ProcA2.e` und vergleichen Sie die Ausgabe mit der Ausgabe unter Aufgabe 1. Diskutieren und erklären Sie was gleich ist und was anders. + ``` + + + ``` +3. Benennen Sie `ChildProcA2.e` auf `ChildProcA2.f` um (Shell Befehl `mv`) und überlegen Sie, was das Programm nun ausgibt. Starten Sie `ProcA2.e` und vergleichen Sie Ihre Überlegungen mit der Programmausgabe. + ``` + + + ``` +4. Nennen Sie das Kindprogramm wieder `ChildProcA2.e` und geben Sie folgenden Befehl ein: `chmod -x ChildProcA2.e`. Starten Sie wiederum `ProcA2.e` und analysieren Sie die Ausgabe von `perror("...")`. Wieso verwenden wir `perror()`? + ``` + + + ``` + +___ + +### 3.3 Aufgabe 3: Prozesshierarchie analysieren + +**Ziele** + +* Verstehen, was `fork()` wirklich macht. +* Verstehen, was Prozesshierarchien sind. + +**Aufgaben** + +1. Studieren Sie zuerst Programm `ProcA3.c` und zeichnen Sie die entstehende Prozesshierarchie (Baum) von Hand auf. Starten Sie das Programm und verifizieren Sie ob Ihre Prozesshierarchie stimmt. +2. Mit dem Befehl `ps f` oder `pstree` können Sie die Prozesshierarchie auf dem Bildschirm ausgeben. Damit die Ausgabe von `pstree` übersichtlich ist, müssen Sie in dem Fenster, wo Sie das Programm `ProcA3.e` starten, zuerst die PID der Shell erfragen, z.B. über `echo $$`. Wenn Sie nun den Befehl `pstree -n -p pid-von-oben` eingeben, wird nur die Prozesshierarchie ausgehend von der Bash Shell angezeigt: `-n` sortiert die Prozesse numerisch, `-p` zeigt für jeden Prozess die PID an. + +**Hinweis:** alle erzeugten Prozesse müssen arbeiten (d.h. nicht terminiert sein), damit die Darstellung gelingt. Wie wird das im gegebenen Programm erreicht? + + +___ + +### 3.4 Aufgabe 4: Zeitlicher Ablauf von Prozessen + +**Ziele** + +* Verstehen, wie Kind- und Elternprozesse zeitlich ablaufen. + +**Aufgaben** + +1. Studieren Sie Programm `ProcA4.c.` Starten Sie nun mehrmals hintereinander das Programm `ProcA4.e` und vergleichen Sie die jeweiligen Outputs (leiten Sie dazu auch die Ausgabe auf verschiedene Dateien um). Was schliessen Sie aus dem Resultat? + ``` + + + ``` +**Anmerkung:** Der Funktionsaufruf `selectCPU(0)` erzwingt die Ausführung des Eltern- und Kindprozesses auf CPU 0 (siehe Modul `setCPU.c`). Die Prozedur `justWork(HARD_WORK)` simuliert CPU-Load durch den Prozess (siehe Modul `workerUtils.c`). + +___ + +### 3.5 Aufgabe 5: Waisenkinder (Orphan Processes) + +**Ziele** +* Verstehen, was mit verwaisten Kindern geschieht. + +**Aufgaben** +1. Analysieren Sie Programm `ProcA5.c`: was läuft ab und welche Ausgabe erwarten Sie? + + ``` + + + ``` +2. Starten Sie `ProcA5.e`: der Elternprozess terminiert: was geschieht mit dem Kind? + + ``` + + + ``` +3. Was geschieht, wenn der Kindprozess vor dem Elternprozess terminiert? Ändern Sie dazu im `sleep()` Befehl die Zeit von 2 Sekunden auf 12 Sekunden und verfolgen Sie mit top das Verhalten der beiden Prozesse, speziell auch die Spalte S. + ``` + + + ``` +___ + +### 3.6 Aufgabe 6: Terminierte, halbtote Prozesse (Zombies) + +**Ziele** + +* Verstehen, was ein Zombie ist. +* Eine Möglichkeit kennenlernen, um Zombies zu verhindern. + +**Aufgaben** + +1. Analysieren Sie das Programm `ProcA6.c`. +2. Starten Sie das Script `mtop` bzw. `mtop aaaa.e`. Es stellt das Verhalten der Prozesse dynamisch dar. + + **Hinweis:** `` = Zombie. +3. Starten Sie `aaaa.e` und verfolgen Sie im `mtop`-Fenster was geschieht. Was beachten Sie? + ``` + + + ``` + +4. In gewissen Fällen will man nicht auf die Terminierung eines Kindes mit `wait()`, bzw. `waitpid()` warten. Überlegen Sie sich, wie Sie in diesem Fall verhindern können, dass ein Kind zum Zombie wird. + ``` + + + ``` + +___ + +### 3.7 Aufgabe 7: Auf Terminieren von Kindprozessen warten + +**Vorbemerkung:** Diese Aufgabe verwendet Funktionen welche erst in der Vorlesung über *Inter-Process-Communication (IPC)* im Detail behandelt werden. + +Sie können diese Aufgabe bis dann aufsparen oder die verwendeten Funktionen selber via `man` Pages im benötigten Umfang kennenlernen: `man 2 kill` und `man 7 signal`. + +**Ziele** +* Verstehen, wie Informationen zu Kindprozessen abgefragt werden können. +* Die Befehle `wait()` und `waitpid()` verwenden können. + +**Aufgaben** +1. Starten Sie das Programm `ProcA7.e` und analysieren Sie wie die Ausgabe im Hauptprogramm zustande kommt und was im Kindprozess `ChildProcA7.c` abläuft. + ``` + + + ``` +2. Starten Sie `ProcA7.e` und danach nochmals mit `1` als erstem Argument. Dieser Argument Wert bewirkt, dass im Kindprozess ein ”Segmentation Error” erzeugt wird, also eine Speicherzugriffsverletzung. Welches Signal wird durch die Zugriffsverletzung an das Kind geschickt? Diese Information finden Sie im Manual mit `man 7 signal`. Schalten Sie nun core dump ein (siehe README) und starten Sie `ProcA7.e 1` erneut und analysieren Sie die Ausgabe. +``` + + +``` +**Hinweis:** ein core Dump ist ein Abbild des Speichers z.B. zum Zeitpunkt, wenn das Programm abstürzt (wie oben mit der Speicher Zugriff Verletzung). Der Dump wird im File **core** abgelegt und kann mit dem **gdb** (GNU-Debugger) gelesen werden (siehe `README`). Tippen Sie nach dem Starten des Command Line UI des `gdb where` gefolgt von list ein, damit sie den Ort des Absturzes sehen. Mit `quit` verlassen Sie **gdb** wieder. + +3. Wenn Sie `ProcA7.e 2` starten, sendet das Kind das Signal 30 an sich selbst. Was geschieht? + ``` + + + ``` +4. Wenn Sie `ProcA7.e 3` starten, sendet ProcA7.e das Signal SIGABRT (abort) an das Kind: was geschieht in diesem Fall? + ``` + + + ``` +5. Mit `ProcA7.e 4` wird das Kind gestartet und terminiert nach 5 Sekunden. Analysieren Sie wie in ProcA7.e der Lauf- bzw. Exit-Zustand des Kindes abgefragt wird (siehe dazu auch `man 3 exit`). + ``` + + + ``` +___ + +### 3.8 Aufgabe 8: Kindprozess als Kopie des Elternprozesses + +**Ziele** +* Verstehen, wie Prozessräume vererbt werden. +* Unterschiede zwischen dem Prozessraum von Eltern und Kindern erfahren. + +**Aufgaben** +1. Analysieren Sie Programm `ProcA8_1.c`: was gibt das Programm aus? + * Starten Sie `ProcA8_1.e `und überprüfen Sie Ihre Überlegungen. + * Waren Ihre Überlegungen richtig? Falls nicht, was könnten Sie falsch überlegt haben? + ``` + + + ``` +2. Analysieren Sie Programm `ProcA8_2.c`: was gibt das Programm aus? + * Starten Sie `ProcA8_2.e` und überprüfen Sie Ihre Überlegungen. + * Waren Ihre Überlegungen richtig? Falls nicht, was könnten Sie falsch gemacht haben? + * Kind und Eltern werden in verschiedener Reihenfolge ausgeführt: ist ein Unterschied ausser der Reihenfolge festzustellen? + ``` + + + ``` +3. Analysieren Sie Programm `ProcA8_3.c` und Überlegen Sie, was in die Datei `AnyOutPut.txt` geschrieben wird, wer schreibt alles in diese Datei (sie wird ja vor `fork()` geöffnet) und wieso ist das so? + * Starten Sie `ProcA8_3.e` und überprüfen Sie Ihre Überlegungen. + * Waren Ihre Überlegungen richtig? Falls nicht, wieso nicht? + ``` + + + ``` + +___ + +### 3.9 Aufgabe 9: Unterschied von Threads gegenüber Prozessen + +**Ziele** +* Den Unterschied zwischen Thread und Prozess kennenlernen. +* Problemstellungen um Threads kennenlernen. +* Die `pthread`-Implementation kennen lernen. + +**Aufgaben** +1. Studieren Sie Programm `ProcA9.c` und überlegen Sie, wie die Programmausgabe aussieht. Vergleichen Sie Ihre Überlegungen mit denjenigen aus Aufgabe 8.2 b) (`Pro-cA8_2.e`). + * Starten Sie `ProcA9.e` und vergleichen das Resultat mit Ihren Überlegungen. + * Was ist anders als bei `ProcA8_2.e`? +``` + + +``` + + +2. Setzen Sie in der Thread-Routine vor dem Befehl `pthread_exit()` eine unendliche Schleife ein, z.B. `while(1) { }`; . + * Starten Sie das Programm und beobachten Sie das Verhalten mit `top`. Was beobachten Sie und was schliessen Sie daraus? + + **Hinweis:** wenn Sie in `top` den Buchstaben H eingeben, werden die Threads einzeln dargestellt. + * Kommentieren Sie im Hauptprogram die beiden `pthread_join()` Aufrufe aus und starten Sie das Programm. Was geschieht? Erklären Sie das Verhalten. + +``` + + +``` + + +___ + +### 3.10 Aufgabe 10 (optional): + +#### 3.10.1 Übersicht +Dieser Teil des Praktikums behandelt spezielle Prozesse: die Dämon Prozesse («daemon pro-cesses»). Es ist gedacht als Zusatz zum Basis Praktikum über Prozesse und Threads. + +Auch dieser Teil ist ein «Analyse»- und «Experimentier»-Praktikum. +___ + +##### 3.10.1.1 Nachweis +Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen. + +Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman. + + +___ + +#### 3.10.2 Lernziele +In diesem Praktikum werden Sie sich mit Dämon Prozessen beschäftigen. +* Sie können die Problemstellung der Dämon Prozesse erklären +* Sie können einen Dämon Prozess kreieren +* Sie können aus dem Dämon Prozess mit der Umgebung kommunizieren +* +___ + +#### 3.10.3 Aufgabe: Dämon Prozesse + +**Ziele** +* Problemstellungen um Daemons kennenlernen: + * wie wird ein Prozess zum Daemon? + * wie erreicht man, dass nur ein Daemon vom gleichen Typ aktiv ist? + * wie teilt sich ein Daemon seiner Umwelt mit? + * wo "lebt" ein Daemon? + +**Einleitung** + +Für diese Aufgabe haben wir einen Daemon implementiert: **MrTimeDaemon** gibt auf Anfrage die Systemzeit Ihres Rechners bekannt. Abfragen können Sie diese Zeit mit dem Programm `WhatsTheTimeMr localhost`. Die Kommunikation zwischen den beiden Prozessen haben wir mit TCP/IP Sockets implementiert. Weitere Infos zum Daemon finden Sie nach den Aufgaben. + +Im Abschnitt 4 finden Sie Zusatzinformationen über diese Implementation eines Dämon Prozesses plus weiterführende Informationen. + +**Aufgaben** + +1. Für die folgende Aufgabe benötigen Sie mindestens zwei Fenster (Kommandozeilen-Konsolen). Übersetzen Sie die Programme mit `make` und starten Sie das Programm **PlapperMaul** in einem der Fenster. Das Programm schreibt (ca.) alle 0.5 Sekunden *Hallo, ich bins.... Pidi* plus seine Prozess-ID auf den Bildschirm. Mit dem Shell Befehl `ps` können Sie Ihre aktiven Prozesse auflisten, auch **PlapperMaul**. Überlegen Sie sich zuerst, was mit **PlapperMaul** geschieht, wenn Sie das Fenster schliessen: läuft **PlapperMaul** weiter? Was geschieht mit **PlapperMaul** wenn Sie sich ausloggen und wieder einloggen? Testen Sie Ihre Überlegungen, in dem Sie die entsprechenden Aktionen durchführen. Stimmen Ihre Überlegungen? + ``` + + + ``` + +2. Starten Sie nun das Programm bzw. den Daemon **MrTimeDaemon**. Stellen Sie die gleichen Überlegungen an wie mit **PlapperMaul** und testen Sie wiederum, ob Ihre Überlegungen stimmen. Ob **MrTimeDaemon** noch läuft können Sie feststellen, indem Sie die Zeit abfragen oder den Befehl `ps ajx | grep MrTimeDaemon` eingeben: was fällt Ihnen am Output auf? Was schliessen Sie aus Ihren Beobachtungen? + ``` + + + ``` + +3. Starten Sie **MrTimeDaemon** erneut, was geschieht? + ``` + + + ``` + +4. Stoppen Sie nun **MrTimeDaemon** mit `killall MrTimeDaemon`. +5. Starten Sie **MrTimeDaemon** und fragen Sie mit `WhatsTheTimeMr localhost` oder mit `WhatsTheTimeMr 127.0.0.1` die aktuelle Zeit auf Ihrem Rechner ab. + + + **Optional:** + Fragen Sie die Zeit bei einem Ihrer Kollegen ab. Dazu muss beim Server (dort wo **MrTimeDaemon** läuft) ev. die Firewall angepasst werden. Folgende Befehle müssen dazu mit **root-Privilegien** ausgeführt werden: + + ```bash + iptables-save > myTables.txt # sichert die aktuelle Firewall + iptables -I INPUT 1 -p tcp --dport 65534 -j ACCEPT + iptables -I OUTPUT 2 -p tcp --sport 65534 -j ACCEPT + ``` + + Nun sollten Sie über die IP-Nummer oder über den Rechner-Namen auf den **TimeServer** mit `WhatsTheTimeMr` zugreifen können. + Die Firewall können Sie mit folgendem Befehl wiederherstellen: + ```bash + iptables-restore myTables.txt + ``` + +6. Studieren Sie `MrTimeDaemon.c`, `Daemonizer.c` und `TimeDaemon.c` und analysieren Sie, wie die Daemonisierung abläuft. Entfernen Sie die Kommentare im Macro `Out-PutPIDs` am Anfang des Moduls `Daemonizer.c`. Übersetzen Sie die Programme mit make und starten Sie `MrTimeDaemon` erneut. Analysieren Sie die Ausgabe, was fällt Ihnen auf? Notieren Sie alle für die vollständige Daemonisierung notwendigen Schritte. + ``` + + + ``` + +7. Setzen Sie beim Aufruf von `Daemonizer()` in `MrTimeDaemon.c` anstelle von `lock-FilePath` den Null-Zeiger `NULL` ein. Damit wird keine lock-Datei erzeugt. Übersetzen Sie die Programme und starten Sie erneut `MrTimedaemon`. Was geschieht bzw. wie können Sie feststellen, was geschehen ist? + + **Hinweis:** lesen Sie das log-File: `/tmp/timeDaemon.log.` + ``` + + + ``` + + Wenn Sie noch Zeit und Lust haben: messen Sie die Zeit, zwischen Start der Zeitanfrage und Eintreffen der Antwort. Dazu müssen Sie die Datei `WhatsTheTimeMr.c` entsprechend anpassen. + +___ + +#### 3.10.4 Zusatzinformationen +___ + +##### 3.10.4.1 Diese Implementation + +Dieser Daemon besteht aus den 3 Komponenten. + +**Hauptprogramm: MrTimeDaemon.c** + +Hier werden die Pfade für die lock-Datei, die log-Datei und der ”Aufenthaltsort” des Daemons gesetzt. Die lock-Datei wird benötigt um sicherzustellen, dass der Daemon nur einmal gestartet werden kann. In die lock-Datei schreibt der Daemon z.B. seine PID und sperrt sie dann für Schreiben. Wird der Daemon ein zweites Mal gestartet und will seine PID in diese Datei schreiben, erhält er eine Fehlermeldung und terminiert (es soll ja nur ein Daemon arbeiten). Terminiert der Daemon, wird die Datei automatisch freigegeben. Weil Daemonen sämtliche Kontakte mit ihrer Umwelt im Normalfall abbrechen und auch kein Kontrollterminal besitzen, ist es sinnvoll, zumindest die Ausgabe des Daemons in eine log-Datei umzuleiten. Dazu stehen einige Systemfunktionen für Logging zur Verfügung. Der Einfachheit halber haben wir hier eine normale Datei im Verzeichnis `/tmp` gewählt. + +> **Anmerkung:** die Wahl des Verzeichnisses `/tmp` für die lock- und log-Datei ist für den normalen Betrieb problematisch, weil der Inhalt dieses Verzeichnisses jederzeit gelöscht werden kann, bzw. darf. Wir haben dieses Verzeichnis gewählt, weil wir die beiden Dateien nur für die kurze Zeit des Praktikums benötigen. + +Der Daemon erbt sein Arbeitsverzeichnis vom Elternprozesse, er sollte deshalb in ein festes Verzeichnis des Systems wechseln, um zu verhindern, dass er sich in einem montierten (gemounteten) Verzeichnis aufhält, das dann beim Herunterfahren nicht demontiert werden könnte (wir haben hier wiederum `/tmp` gewählt). + +**Daemonizer: Daemonizer.c** + +Der Daemonizer macht aus dem aktuellen Prozess einen Daemon. Z.B. sollte er Signale (eine Art Softwareinterrupts) ignorieren: wenn Sie die CTRL-C Taste während dem Ausführen eines Vordergrundprozess drücken, erhält dieser vom Betriebssystem das Signal SIGINT und bricht seine Ausführung ab. Weiter sollte er die Dateierzeugungsmaske auf 0 setzen (Dateizugriffsrechte), damit kann er beim Öffnen von Dateien beliebige Zugriffsrechte verlangen (die Dateierzeugungsmaske erbt er vom Elternprozess). Am Schluss startet der Daemonizer das eigentliche Daemonprogramm: TimeDaemon.e. + +**Daemonprogramm: TimeDaemon.c** + +Das Daemonprogramm wartet in einer unendlichen Schleife auf Anfragen zur Zeit und schickt die Antwort an den Absender zurück. Die Datenkommunikation ist, wie schon erwähnt, mit Sockets implementiert, auf die wir aber im Rahmen dieses Praktikums nicht weiter eingehen wollen (wir stellen lediglich Hilfsfunktionen zur Verfügung). +___ + +##### 3.10.4.2 Zusatzinformation zu Dämon Prozessen + +Dämonen oder englisch Daemons sind eine spezielle Art von Prozessen, die vollständig unabhängig arbeiten, d.h. ohne direkte Interaktion mit dem Anwender. Dämonen sind Hintergrundprozesse und terminieren i.A. nur, wenn das System heruntergefahren wird oder abstürzt. Dämonen erledigen meist Aufgaben, die periodisch ausgeführt werden müssen, z.B. Überwachung von Systemkomponenten, abfragen, ob neue Mails angekommen sind, etc. + +Ein typisches Beispiel unter Unix ist der Printer Daemon `lpd`, der periodisch nachschaut, ob ein Anwender eine Datei zum Ausdrucken hinterlegt hat. Wenn ja, schickt er die Datei auf den Drucker. + +Hier wird eine weitere Eigenschaft von Daemons ersichtlich: meist kann nur ein Dämon pro Aufgabe aktiv sein: stellen Sie sich vor, was passiert, wenn zwei Druckerdämonen gleichzeitig arbeiten. Andererseits muss aber auch dafür gesorgt werden, dass ein Dämon wieder gestartet wird, falls er stirbt. + +___ + + + +___ + +## 4. Bewertung + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können die gestellten Fragen erklären. | | +| 1 | Prozess mit `fork()` erzeugen | 0.5 | +| 2 | Prozess mit `fork()` und `exec()`: Programm Image ersetzen | 0.5 | +| 3 | Prozesshierarchie analysieren | 0.5 | +| 4 | Zeitlicher Ablauf von Prozessen | 0.5 | +| 5 | Waisenkinder (Orphan Processes) | 0.5 | +| 6 | Terminierte, halbtote Prozesse (Zombies) | 0.5 | +| 7 | Auf Terminieren von Kindprozessen warten | 0.5 | +| 8 | Kindprozess als Kopie des Elternprozesses | 0.5 | +| 9 | Unterschied von Threads gegenüber Prozessen | 0.5 | +| 10 | Dämon Prozesse | (4) | + + +___ +Version: 11.01.2022 diff --git a/build/html/_sources/P07_Prozesse_und_Threads/README_P02.md.txt b/build/html/_sources/P07_Prozesse_und_Threads/README_P02.md.txt new file mode 100644 index 0000000..13599b8 --- /dev/null +++ b/build/html/_sources/P07_Prozesse_und_Threads/README_P02.md.txt @@ -0,0 +1,149 @@ +# 09/02 - Dämon Prozesse + +___ + +![](./daemon.png) + +___ + +## 1. Übersicht +Dieser Teil des Praktikums behandelt spezielle Prozesse: die Dämon Prozesse («daemon pro-cesses»). Es ist gedacht als Zusatz zum Basis Praktikum über Prozesse und Threads. + +Auch dieser Teil ist ein «Analyse»- und «Experimentier»-Praktikum. + + +___ + +### 1.1 Nachweis +Dieses Praktikum ist eine leicht abgewandelte Variante des ProcThreads Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen. + +Als Autoren des BSY Praktikums sind genannt: M. Thaler, J. Zeman. + +___ + +## 2. Lernziele +In diesem Praktikum werden Sie sich mit Dämon Prozessen beschäftigen. +* Sie können die Problemstellung der Dämon Prozesse erklären +* Sie können einen Dämon Prozess kreieren +* Sie können aus dem Dämon Prozess mit der Umgebung kommunizieren +* +___ + +## 3. Aufgabe: Dämon Prozesse + +**Ziele** +* Problemstellungen um Daemons kennenlernen: + * wie wird ein Prozess zum Daemon? + * wie erreicht man, dass nur ein Daemon vom gleichen Typ aktiv ist? + * wie teilt sich ein Daemon seiner Umwelt mit? + * wo "lebt" ein Daemon? + +**Einleitung** + +Für diese Aufgabe haben wir einen Daemon implementiert: **MrTimeDaemon** gibt auf Anfrage die Systemzeit Ihres Rechners bekannt. Abfragen können Sie diese Zeit mit dem Programm `WhatsTheTimeMr localhost`. Die Kommunikation zwischen den beiden Prozessen haben wir mit TCP/IP Sockets implementiert. Weitere Infos zum Daemon finden Sie nach den Aufgaben. + +Im Abschnitt 4 finden Sie Zusatzinformationen über diese Implementation eines Dämon Prozesses plus weiterführende Informationen. + +**Aufgaben** + +1. Für die folgende Aufgabe benötigen Sie mindestens zwei Fenster (Kommandozeilen-Konsolen). Übersetzen Sie die Programme mit `make` und starten Sie das Programm **PlapperMaul** in einem der Fenster. Das Programm schreibt (ca.) alle 0.5 Sekunden *Hallo, ich bins.... Pidi* plus seine Prozess-ID auf den Bildschirm. Mit dem Shell Befehl `ps` können Sie Ihre aktiven Prozesse auflisten, auch **PlapperMaul**. Überlegen Sie sich zuerst, was mit **PlapperMaul** geschieht, wenn Sie das Fenster schliessen: läuft **PlapperMaul** weiter? Was geschieht mit **PlapperMaul** wenn Sie sich ausloggen und wieder einloggen? Testen Sie Ihre Überlegungen, in dem Sie die entsprechenden Aktionen durchführen. Stimmen Ihre Überlegungen? +``` + + +``` + +2. Starten Sie nun das Programm bzw. den Daemon **MrTimeDaemon**. Stellen Sie die gleichen Überlegungen an wie mit **PlapperMaul** und testen Sie wiederum, ob Ihre Überlegungen stimmen. Ob **MrTimeDaemon** noch läuft können Sie feststellen, indem Sie die Zeit abfragen oder den Befehl `ps ajx | grep MrTimeDaemon` eingeben: was fällt Ihnen am Output auf? Was schliessen Sie aus Ihren Beobachtungen? +``` + + +``` + +3. Starten Sie **MrTimeDaemon** erneut, was geschieht? +``` + + +``` + +4. Stoppen Sie nun **MrTimeDaemon** mit `killall MrTimeDaemon`. +5. Starten Sie **MrTimeDaemon** und fragen Sie mit `WhatsTheTimeMr localhost` oder mit `WhatsTheTimeMr 127.0.0.1` die aktuelle Zeit auf Ihrem Rechner ab. + + +**Optional:** +Fragen Sie die Zeit bei einem Ihrer Kollegen ab. Dazu muss beim Server (dort wo **MrTimeDaemon** läuft) ev. die Firewall angepasst werden. Folgende Befehle müssen dazu mit **root-Privilegien** ausgeführt werden: + +```bash +iptables-save > myTables.txt # sichert die aktuelle Firewall +iptables -I INPUT 1 -p tcp --dport 65534 -j ACCEPT +iptables -I OUTPUT 2 -p tcp --sport 65534 -j ACCEPT +``` + +Nun sollten Sie über die IP-Nummer oder über den Rechner-Namen auf den **TimeServer** mit `WhatsTheTimeMr` zugreifen können. +Die Firewall können Sie mit folgendem Befehl wiederherstellen: +```bash +iptables-restore myTables.txt +``` + +6. Studieren Sie `MrTimeDaemon.c`, `Daemonizer.c` und `TimeDaemon.c` und analysieren Sie, wie die Daemonisierung abläuft. Entfernen Sie die Kommentare im Macro `Out-PutPIDs` am Anfang des Moduls `Daemonizer.c`. Übersetzen Sie die Programme mit make und starten Sie `MrTimeDaemon` erneut. Analysieren Sie die Ausgabe, was fällt Ihnen auf? Notieren Sie alle für die vollständige Daemonisierung notwendigen Schritte. +``` + + +``` + +7. Setzen Sie beim Aufruf von `Daemonizer()` in `MrTimeDaemon.c` anstelle von `lock-FilePath` den Null-Zeiger `NULL` ein. Damit wird keine lock-Datei erzeugt. Übersetzen Sie die Programme und starten Sie erneut `MrTimedaemon`. Was geschieht bzw. wie können Sie feststellen, was geschehen ist? + + **Hinweis:** lesen Sie das log-File: `/tmp/timeDaemon.log.` +``` + + +``` + +Wenn Sie noch Zeit und Lust haben: messen Sie die Zeit, zwischen Start der Zeitanfrage und Eintreffen der Antwort. Dazu müssen Sie die Datei `WhatsTheTimeMr.c` entsprechend anpassen. + +___ + +## 4. Zusatzinformationen +___ + +### 4.1 Diese Implementation + +Dieser Daemon besteht aus den 3 Komponenten. + +**Hauptprogramm: MrTimeDaemon.c** + +Hier werden die Pfade für die lock-Datei, die log-Datei und der ”Aufenthaltsort” des Daemons gesetzt. Die lock-Datei wird benötigt um sicherzustellen, dass der Daemon nur einmal gestartet werden kann. In die lock-Datei schreibt der Daemon z.B. seine PID und sperrt sie dann für Schreiben. Wird der Daemon ein zweites Mal gestartet und will seine PID in diese Datei schreiben, erhält er eine Fehlermeldung und terminiert (es soll ja nur ein Daemon arbeiten). Terminiert der Daemon, wird die Datei automatisch freigegeben. Weil Daemonen sämtliche Kontakte mit ihrer Umwelt im Normalfall abbrechen und auch kein Kontrollterminal besitzen, ist es sinnvoll, zumindest die Ausgabe des Daemons in eine log-Datei umzuleiten. Dazu stehen einige Systemfunktionen für Logging zur Verfügung. Der Einfachheit halber haben wir hier eine normale Datei im Verzeichnis `/tmp` gewählt. + +> **Anmerkung:** die Wahl des Verzeichnisses `/tmp` für die lock- und log-Datei ist für den normalen Betrieb problematisch, weil der Inhalt dieses Verzeichnisses jederzeit gelöscht werden kann, bzw. darf. Wir haben dieses Verzeichnis gewählt, weil wir die beiden Dateien nur für die kurze Zeit des Praktikums benötigen. + +Der Daemon erbt sein Arbeitsverzeichnis vom Elternprozesse, er sollte deshalb in ein festes Verzeichnis des Systems wechseln, um zu verhindern, dass er sich in einem montierten (gemounteten) Verzeichnis aufhält, das dann beim Herunterfahren nicht demontiert werden könnte (wir haben hier wiederum `/tmp` gewählt). + +**Daemonizer: Daemonizer.c** + +Der Daemonizer macht aus dem aktuellen Prozess einen Daemon. Z.B. sollte er Signale (eine Art Softwareinterrupts) ignorieren: wenn Sie die CTRL-C Taste während dem Ausführen eines Vordergrundprozess drücken, erhält dieser vom Betriebssystem das Signal SIGINT und bricht seine Ausführung ab. Weiter sollte er die Dateierzeugungsmaske auf 0 setzen (Dateizugriffsrechte), damit kann er beim Öffnen von Dateien beliebige Zugriffsrechte verlangen (die Dateierzeugungsmaske erbt er vom Elternprozess). Am Schluss startet der Daemonizer das eigentliche Daemonprogramm: TimeDaemon.e. + +**Daemonprogramm: TimeDaemon.c** + +Das Daemonprogramm wartet in einer unendlichen Schleife auf Anfragen zur Zeit und schickt die Antwort an den Absender zurück. Die Datenkommunikation ist, wie schon erwähnt, mit Sockets implementiert, auf die wir aber im Rahmen dieses Praktikums nicht weiter eingehen wollen (wir stellen lediglich Hilfsfunktionen zur Verfügung). +___ + +### 4.2 Zusatzinformation zu Dämon Prozessen + +Dämonen oder englisch Daemons sind eine spezielle Art von Prozessen, die vollständig unabhängig arbeiten, d.h. ohne direkte Interaktion mit dem Anwender. Dämonen sind Hintergrundprozesse und terminieren i.A. nur, wenn das System heruntergefahren wird oder abstürzt. Dämonen erledigen meist Aufgaben, die periodisch ausgeführt werden müssen, z.B. Überwachung von Systemkomponenten, abfragen, ob neue Mails angekommen sind, etc. + +Ein typisches Beispiel unter Unix ist der Printer Daemon `lpd`, der periodisch nachschaut, ob ein Anwender eine Datei zum Ausdrucken hinterlegt hat. Wenn ja, schickt er die Datei auf den Drucker. + +Hier wird eine weitere Eigenschaft von Daemons ersichtlich: meist kann nur ein Dämon pro Aufgabe aktiv sein: stellen Sie sich vor, was passiert, wenn zwei Druckerdämonen gleichzeitig arbeiten. Andererseits muss aber auch dafür gesorgt werden, dass ein Dämon wieder gestartet wird, falls er stirbt. + +___ + +## 5. Bewertung + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können die gestellten Fragen erklären. | | +| 1 | Dämon Prozesse | 4 | + + +___ +Version: 11.01.2022 \ No newline at end of file diff --git a/build/html/_sources/P08_Sync/README.md.txt b/build/html/_sources/P08_Sync/README.md.txt new file mode 100644 index 0000000..e66b5c2 --- /dev/null +++ b/build/html/_sources/P08_Sync/README.md.txt @@ -0,0 +1,187 @@ +# 08 - Synchronisationsprobleme + +___ + +## 1. Übersicht + +![](./synchronisationsprobleme.png) + +[Quelle: https://commons.wikimedia.org/wiki/File:Velgast-suedbahn.jpg](https://commons.wikimedia.org/wiki/File:Velgast-suedbahn.jpg) + +In diesem Praktikum lernen sie zuerst am Beispiel eines Kaffee-Automaten verschiedene grundlegende Synchronisationsprobleme kennen und mit Hilfe von Locks (Mutexes) und Semaphoren lösen: + - gegenseitiger Ausschluss mit einem Lock + - Erzwingen einer einfachen Reihenfolge + - Erzwingen einer erweiterten Reihenfolge + +Im zweiten Teil werden sie auf Basis dieser Grundlagen ein komplexeres Synchronisationsproblem bearbeiten, diesmal am Beispiel von Bank Transaktionen. +___ + +### 1.1 Nachweis +Dieses Praktikum ist eine leicht abgewandelte Variante des Sync Praktikum des Moduls BSY, angepasst an die Verhältnisse des SNP Moduls. Die Beispiele und Beschreibungen wurden, wo möglich, eins-zu-ein übernommen. + +Als Autor des BSY Praktikums ist genannt: M. Thaler. + +___ + +## 2. Lernziele +In diesem Praktikum werden sie Synchronisationsprobleme lösen +- Sie wissen wie man systematisch Synchronisationsprobleme analysiert +- Sie wissen wann ein potentieller Deadlock entstehen kann +- Sie können Mutex mit Threads anwenden +- Sie können Semaphoren mit Prozessen anwenden + +___ + +## 3. Einführung +Das Lösen von Synchronisationsproblemen ist oft nicht einfach, weil Prozesse bzw. Threads gleichzeitig ablaufen, ihre Aktivitäten jedoch nach Vorgaben koordiniert werden müssen: man verliert schnell den Überblick. Systematisches Vorgehen mit Aufzeichnen der Abläufe und Synchronisationsbedingungen bewährt ich sich in diesem Fall. + +___ + +### 3.1 Wie löst man Synchronisationsprobleme? +Gehen sie beim Lösen von Synchronisationsproblemen in folgenden Schritten vor: + +- **Schritt 1: Prozesse (Threads) der Problemstellung identifizieren.** + Prozesse sind die Aktivitäten, die gleichzeitig ausgeführt werden. In diesem Sinne sind sie eigenständige Ausführungs-Einheiten, deren zeitliches Verhalten synchronisiert werden muss. +- **Schritt 2: Ausführungsschritte der einzelnen Prozesse (Threads) ermitteln.** + Erstellen sie eine Liste mit einer Spalte für jeden Prozess. Notieren sie für jeden Prozess stichwortartig die wesentlichen Aktionen in der gewünschten zeitlichen Reihenfolge. Tragen sie noch keine Synchronisationsoperationen ein, sondern Texte wie warten auf Geld, etc. Übertragen sie anschliessend die Liste in einen Ablaufgraphen (Siehe Beispiel in Abbildung 1). +- **Schritt 3: Synchronisationsbedingungen ermitteln.** + Eine Synchronisationsbedingung ist eine zeitliche Beziehung (Abhängigkeit) zwischen Aktionen verschiedener Prozesse, die für das korrekte Arbeiten erforderlich ist. Zeichnen sie diese Beziehungen mit Pfeilen in den Ablaufgraphen aus Schritt 2 ein (Siehe Abbildung 1). +- **Schritt 4: Benötigte Semaphore definieren.** + Für jede Synchronisationsbedingung wird ein eigener Semaphor benötigt. Notieren sie für jeden Semaphor einen Namen und den Wert, mit dem er initialisiert werden muss. +- **Schritt 5: Prozesse mit Semaphore Operationen ergänzen.** + Erweitern sie nun alle Prozesse aus Schritt 2 mit den notwendigen Semaphore Operati-onen (Siehe Pseudocode in Abbildung 1). +- **Schritt 6: Implementation.** + Implementieren und testen sie das vollständige Programm. + +![](./coffee_customer.png) + + +```c +coin = sem_open(...,0); +coffee = sem_open(...,0); +``` + +Ablaufgraph und Pseudocode für 2 Prozesse und zwei Semaphore +![](./sequence_graph.png) + + + + +## 4. Der Kaffee-Automat +Als Beispiel verwenden wir einen Automaten, der Kaffee verkauft. Der Kunde muss zum Kauf eines Kaffees zuerst eine bzw. mehrere Münzen einwerfen und anschliessend den gewünsch-ten Kaffee wählen. Der Automat gibt dann das entsprechende Getränk aus. + +Im ersten Beispiel werden der Automat und die Kunden mit Threads modelliert und tauschen Daten über gemeinsame Speichervariablen aus. Im zweiten und dritten Beispiel werden der Automat und die Kunden mit Prozessen modelliert, dabei wird der Ablauf mit Hilfe von Sema-phoren gesteuert bzw. erzwungen. + +**Hinweis:** die Programme zu den folgenden Aufgaben können alle mit **startApp.e** gestartet werden. Dieses Programm startet und stoppt Threads und Prozesse, alloziert und dealloziert die Ressourcen (Mutexes, Semaphore). + +___ + +### 4.1 Aufgabe: Mutual Exclusion +Greifen mehrere Threads (oder Prozesse) auf gemeinsame Daten zu, können sogenannte Race Conditions entstehen. Das Resultat ist in diesem Fall abhängig von der Reihenfolge, in der die Threads (Prozesse) ausgeführt werden. + +Im vorliegenden Beispiel wirft der Kunde eine 1 Euro Münze ein und drückt anschliessend auf eine von zwei Kaffeewahltasten. Dabei wird die Anzahl Münzen (*coinCount*) und die gewählte Kaffeesorte (*selCount1*, *selCount2*) inkrementiert. Diese Variablen sind in der Datenstruktur *cData* abgelegt, auf die gemeinsam Kaffee-Automat und Kunden zugreifen können. Der Auto-mat überprüft, ob die Anzahl Münzen und die Anzahl der Kaffeewahlen gleich gross sind, falls nicht, wird eine Fehlermeldung ausgegeben und alle Zähler auf *Null* gesetzt. + +#### Aufgaben + +1. Übersetzen sie die Programme im Verzeichnis *mutex* mit *make* und starten sie den Kaffee-Automaten mit **startApp.e** mehrmals hintereinander. + Analysieren sie die Datenwerte in den Fehlermeldungen, beschreiben sie was die Gründe dafür sind bzw. sein können. + +2. Schützen sie nun den Zugriff auf die gemeinsamen Daten mit einem Mutex so, dass alle Threads eine konsistente Sicht der Daten haben. +Wir haben für sie einen Mutex vorbereitet: die Datenstruktur *cData* enthält die Mutex-Variable *mutex*, die in **startApp.c** initialisiert wird. Die Funktionen für das Schliessen und das Öffnen des Mutex (Locks) aus der *pthread* Bibliothek sind: +```c +pthread mutex lock(&(cD->lock)); +``` + - und +```c +pthread mutex unlock(&(cD->lock)); +``` +Überprüfen sie, ob der Kaffee-Automat nun keine Fehlermeldungen mehr ausgibt. Erhö-hen sie dazu auch die Anzahl Kunden *CUSTOMERS* in **commonDefs.h**, z.B. auf 10. + +3. Im Thread des Kaffee-Automaten wird an verschiedenen Orten mehrmals auf die gemeinsamen Daten in *cD* zugegriffen. Wenn sie die gemeinsamen Daten in lokale Variablen kopieren und dann nur noch auf diese lokalen Variablen zugreifen würden, könn-ten sie dann auf die Synchronisation mit dem Mutex verzichten? + +4. Wie oft kann ein einzelner Kunde einen Kaffee beziehen, bis der nächste Kunde an die Reihe kommt? Hier reicht eine qualitative Aussage. + +### 4.2 Aufgabe: Einfache Reihenfolge +Wie sie im ersten Beispiel festgestellt haben, verhindert ein Mutex zwar, dass Race Conditions auftreten, die Verarbeitungsreihenfolge der Threads lässt sich jedoch nicht beeinflussen und ist zufällig. +Im Folgenden soll eine erzwungene Verarbeitungsreihenfolge implementiert werden: +- Ein Kunde benutzt den Automat für einen Kaffeekauf exklusiv, d.h. alle Schritte des Kunden werden innerhalb eines Mutexes ausgeführt. Ist ein Kunde an der Reihe, wartet er bis der Automat bereit ist, wirft eine Münze ein, wartet auf den Kaffee und gibt anschlies-send den Automaten für den nächsten Kunden frei. + +- Der Automat meldet zuerst in einer Endlos-Schleife, dass er für die Geld-Eingabe bereit ist, wartet dann auf die Eingabe einer Münze, gibt den Kaffee aus und meldet anschliessend wieder, wenn er bereit ist, etc. + +Für die Lösung dieses Problems benötigen wir Semaphore, die, im Gegensatz zu Mutexes, auch in verschiedenen Prozessen gesetzt bzw. zurückgesetzt werden dürfen. Den Kaffee-Automat und die Kunden implementieren wir mit Prozessen. sie finden die entsprechenden Prozesse im Verzeichnis **basicSequence**. + +#### Aufgaben +1. Beschreiben sie den Kaffee-Automaten mit Hilfe der 6 Schritte aus Abschnitt 3 auf Papier, dokumentieren sie dabei alle Schritte schriftlich. +2. Implementieren sie nun den Kaffee-Automaten. Ergänzen sie dazu den *coffeeTeller* und den *customer* Prozess so mit vier Semaphoren, dass die vorgegebenen Ablaufbedingungen eingehalten werden. Mit welchen Werten müssen die Semaphore initialisiert werden? +Wir haben für sie vier Semaphore vorbereitet: Achtung, sie sind aber noch auskommentiert (siehe commonDefs.h und startApp.c. Die benötigten Semaphor-Funktionen aus der POSIX Bibliothek sind: +```c +sem_wait(&semaphor); +``` +und +```c +sem_post(&semaphor); +``` +Analysieren sie die Ausgabe der Prozesse (mehrmals starten). Was fällt auf? + +3. Gibt Ihr Programm den Output in der korrekten Reihenfolge aus? Falls nicht, wie könnte das gelöst werden? + +### 4.3 Aufgabe: Erweiterte Reihenfolge +Die Preise steigen dauernd ... auch der Kaffee wird immer teurer, er kostet nun 3 Euro. Da der Automat nur 1 Euro Stücke annehmen kann, muss der Kunde 3 Münzen einwerfen. Erweitern sie die Prozesse aus Aufgabe 4.2 so, dass eine vordefinierte Anzahl Münzen eingegeben werden muss (die Anzahl Münzen ist in *commonDefs.h* als *NUM_COINS* definiert). Verwenden sie keine zusätzlichen Semaphore, sondern nutzen sie, dass wir Counting Semaphore verwenden. Die vordefinierten Prozesse finden sie im Verzeichnis *advancedSequence*. +#### Aufgabe +- Passen sie den coffeeTeller und den customer Prozess so an, dass der Kunde mehrere Münzen einwerfen muss, bis der Automat einen Kaffee ausgeben kann. + +**Hinweis:** POSIX Semaphore sind Counting Semaphore, können aber nicht auf vordefinierte Werte gesetzt werden (ausser bei der Initialisierung). Abhilfe schafft hier das mehrmalige Aufrufen von *sem_post()*, z.B. in einer for-Schleife. + +### 4.4 Zusammenfassung +Wir haben drei grundlegenden Typen von Synchronisationsproblemen kennen gelernt: +- **Mutex** nur ein Prozess bzw. Thread kann gleichzeitig auf gemeinsame Daten zugreifen. + - Beispiel: entweder liest der Kaffee-Automat die Daten oder ein Kunde verändert sie. +- **Einfache Reihenfolge** ein Prozess wartet auf die Freigabe durch einen anderen Prozess. + - Beispiel: der Kaffee-Automat wartet auf die Eingabe einer Münze. +- **Erweiterte Reihenfolge** ein Prozess wartet auf mehrere Freigaben durch einen anderen Pro-zess. + - Beispiel: der Kaffee-Automat wartet auf die Eingabe von drei Münzen. + +___ + +## 5. International Banking +Die International Bank of Transfer (IBT) besitzt in 128 Ländern Filialen und stellt für 2048 spezielle Handels-Kunden in jeder Filiale ein Konto zur Verfügung. Gelder dieser Kunden werden dauernd zwischen den Filialen hin und her transferiert, dazu beschäftigt die Bank sogenannte Pusher. Pusher heben Geldbeträge von Konten in einer Filiale ab und buchen sie auf den entsprechenden Konten in irgendeiner (auch in der eigenen) Filiale wieder ein. Die Beträge liegen zwischen 1000 und 100’000 Dollar und werden zufällig ausgewählt, die Wahl der beiden Filialen ist ebenfalls zufällig. + +### 5.1 Implementation +Im Folgenden arbeiten wir mit einer *pthread*-basierten Implementation der IBT, die Pusher werden dabei mit Threads implementiert. Die Filialen der Bank sind als Array von Strukturen implementiert, wobei pro Filiale ein Lock (*branchLock*) und ein Array von Konten (Accounts) definiert ist. Die Konten sind wiederum Strukturen mit dem Kontostand (*account*) und dem Lock (*acntLock*), siehe dazu auch den Source Code. Die Zugriffe auf die Gelder sind imple-mentiert (Funktionen *withdraw()*, *deposit()*, *transfer()*), aber nicht synchronisiert. +**Hinweis:** es ist von Vorteil hier mit mehreren CPUs zu arbeiten. Falls sie eine VM verwenden, setzen sie die Anzahl CPUs auf das Maximum. + + +### 5.2 Aufgabe: Konto Synchronisation +1. Wechseln sie ins Verzeichnis **banking/a1**, übersetzen sie das Programm und starten sie es mit dem Skript `./startApp`. Analysieren und erklären sie die Resultate. Notie-ren sie sich zudem die Laufzeiten für 1, 2 und 4 Threads. +2. Synchronisieren sie die Kontenzugriffe so, dass möglichst viele Zugriffe gleichzeitig ausgeführt werden können und die Zugriffe atomar sind. Sie dürfen nur eines der beiden Locks *branchLock* bzw. *acntLock* verwenden: welches wählen sie und wieso? Be-gründen sie ihre Antwort und testen sie ihre Lösung. + +### 5.3 Aufgabe: Filialen Zugriff in Critical Section +Ihr Chef meint, dass es wohl aus Sicherheitsgründen besser wäre, sowohl die Filialen und die jeweiligen Kontenzugriffen zu ”locken”. + 1. Wechseln sie ins Verzeichnis banking/a2 und kopieren sie banking.c aus Aufgabe 5.2. Implementieren sie diese zusätzlichen Anforderungen. Analysieren sie die Resultate. Was stellen sie fest im Vergleich mit den Resultaten aus der Aufgabe 5.2? Was raten sie ihrem Chef? + 2. Ein Kollege meint, es wäre effizienter beim Abheben des Betrags zuerst das Konto zu locken und dann die Filiale, hingegen beim Einbuchen zuerst die die Filiale und dann das Konto. Was für eine Antwort geben sie ihrem Kollegen?**Hinweis:** falls sie nicht sicher sind: probieren sie es aus. + +### 5.4 Aufgabe: Refactoring der Synchronisation +Das International Banking Committe (IBC) erlässt neue Richtlinien, die unter anderem fordern, dass die Gesamtbilanz einer Bank über sämtliche Filialen zu jeder Zeit konsistent sein muss. +1. Erklären sie wieso die Implementationen aus Aufgabe 5.2 und 5.3 diese Anforderungen nicht erfüllen. +2. Ihr Entwicklungsteam kommt zum Schluss, dass den Pushern neu nur noch eine Funktion *transfer()* für die Überweisung von Beträgen zwischen den Filialen und Konten zur Verfügung gestellt werden darf. +Welche Locks bzw. welches Lock muss verwendet werden, damit die Forderung des IBC erfüllt werden kann? Wechseln sie ins Verzeichnis *banking/a3* und ergänzen sie die Funktion *transfer()* in banking.c um die entsprechenden Lock-Funktionen. +Wichtiger +**Hinweis:** es darf kein neues Lock eingeführt werden und die Gesamtbilanz über sämtliche Filialen muss jederzeit konsistent sein. +3. Testen und analysieren sie das Programm und vergleichen sie die Resultate (Funktionalität, Laufzeit) mit den Lösungen aus Aufgabe 5.2 und 5.3. Notieren sie sich, was ihnen bei dieser Aufgabe wichtig erscheint. +4. +___ + + +## 6. Bewertung + +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. +| Aufgabe | Kriterium | Gewicht | +| :-- | :-- | :-- | +| | Sie können die gestellten Fragen erklären. | | +| 4 | 4.1 Aufgabe: Mutual Exclusion
4.2 Aufgabe: Einfache Reihenfolge
4.3 Aufgabe: Erweiterte Reihenfolge | 4 | +| 5 | 5.2 Aufgabe: Konto Synchronisation
5.3 Aufgabe: Filialen Zugriff in Critical Section
5.4 Aufgabe: Refactoring der Synchronisation | 4 | + + +___ +___ +Version: 18.08.2021 diff --git a/build/html/_sources/P09_File_Operations/README.md.txt b/build/html/_sources/P09_File_Operations/README.md.txt new file mode 100644 index 0000000..8ecb004 --- /dev/null +++ b/build/html/_sources/P09_File_Operations/README.md.txt @@ -0,0 +1,34 @@ +# 09 - File Operations + +___ + + +___ + +## 1. Übersicht + + +___ + +## 2. Lernziele + + + +___ + +## 3. Aufgabe 1: + +___ + +## 4. Bewertung +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| 1 | - | - | + + + +___ +Version: 16.02.2022 diff --git a/build/html/_sources/P10_IPC/README.md.txt b/build/html/_sources/P10_IPC/README.md.txt new file mode 100644 index 0000000..bacd4fa --- /dev/null +++ b/build/html/_sources/P10_IPC/README.md.txt @@ -0,0 +1,31 @@ +# 10 - IPC + +___ + +## 1. Übersicht + + +___ + +## 2. Lernziele + + + +___ + +## 3. Aufgabe 1: + +___ + +## 4. Bewertung +Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden. + +| Aufgabe | Kriterium | Punkte | +| :-- | :-- | :-- | +| | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | | +| 1 | - | - | + + + +___ +Version: 16.02.2022 diff --git a/build/html/_sources/index.rst.txt b/build/html/_sources/index.rst.txt new file mode 100644 index 0000000..749ecfe --- /dev/null +++ b/build/html/_sources/index.rst.txt @@ -0,0 +1,26 @@ +.. demo documentation master file, created by + sphinx-quickstart on Wed Feb 9 08:17:44 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + +SNP Laboratories +---------------------------------- + +.. toctree:: + P01_Erste_Schritte_mit_C/README.md + P02_Funktionen_Datentyp_enum/README.md + P03_Bit_Operation_struct_typedef/README.md + P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.md + P05_TicTacToe/README.md + P06_Personen_Verwaltung_Linked_List/README.md + P07_Prozesse_und_Threads/README.md + P08_Sync/README.md + P09_File_Operations/README.md + P10_IPC/README.md + \ No newline at end of file diff --git a/build/html/_static/alabaster.css b/build/html/_static/alabaster.css new file mode 100644 index 0000000..0eddaeb --- /dev/null +++ b/build/html/_static/alabaster.css @@ -0,0 +1,701 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/build/html/_static/basic.css b/build/html/_static/basic.css new file mode 100644 index 0000000..bf18350 --- /dev/null +++ b/build/html/_static/basic.css @@ -0,0 +1,906 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/build/html/_static/custom.css b/build/html/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/build/html/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/build/html/_static/doctools.js b/build/html/_static/doctools.js new file mode 100644 index 0000000..e509e48 --- /dev/null +++ b/build/html/_static/doctools.js @@ -0,0 +1,326 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { + this.initOnKeyListeners(); + } + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + var url = new URL(window.location); + url.searchParams.delete('highlight'); + window.history.replaceState({}, '', url); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keydown(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box, textarea, dropdown or button + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' + && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey + && !event.shiftKey) { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + break; + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + break; + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/build/html/_static/documentation_options.js b/build/html/_static/documentation_options.js new file mode 100644 index 0000000..4daa6b5 --- /dev/null +++ b/build/html/_static/documentation_options.js @@ -0,0 +1,12 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false +}; \ No newline at end of file diff --git a/build/html/_static/file.png b/build/html/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/build/html/_static/file.png differ diff --git a/build/html/_static/jquery-3.5.1.js b/build/html/_static/jquery-3.5.1.js new file mode 100644 index 0000000..5093733 --- /dev/null +++ b/build/html/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/index.html b/build/html/index.html new file mode 100644 index 0000000..a400845 --- /dev/null +++ b/build/html/index.html @@ -0,0 +1,319 @@ + + + + + + + + + SNP Laboratories — SNP Labs documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+
+
+

SNP Laboratories

+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/objects.inv b/build/html/objects.inv new file mode 100644 index 0000000..aadbb09 Binary files /dev/null and b/build/html/objects.inv differ diff --git a/build/html/search.html b/build/html/search.html new file mode 100644 index 0000000..c3f3c2c --- /dev/null +++ b/build/html/search.html @@ -0,0 +1,138 @@ + + + + + + + + Search — SNP Labs documentation + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + + +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/build/html/searchindex.js b/build/html/searchindex.js new file mode 100644 index 0000000..10d4062 --- /dev/null +++ b/build/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["P01_Erste_Schritte_mit_C/README","P02_Funktionen_Datentyp_enum/README","P02_Funktionen_Datentyp_enum/README_solution","P03_Bit_Operation_struct_typedef/README","P04_Modularisieren_von_C_Code/README","P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code","P05_TicTacToe/P05_TicTacToe","P05_TicTacToe/README","P06_Personen_Verwaltung_Linked_List/README","P07_Prozesse_und_Threads/README","P07_Prozesse_und_Threads/README_P02","P08_Sync/README","P09_File_Operations/README","P10_IPC/README","index"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":4,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["P01_Erste_Schritte_mit_C\\README.md","P02_Funktionen_Datentyp_enum\\README.md","P02_Funktionen_Datentyp_enum\\README_solution.md","P03_Bit_Operation_struct_typedef\\README.md","P04_Modularisieren_von_C_Code\\README.md","P04_Modularisieren_von_C_Code\\new_P04\\P04_Modularisieren_von_C_Code.md","P05_TicTacToe\\P05_TicTacToe.rst","P05_TicTacToe\\README.md","P06_Personen_Verwaltung_Linked_List\\README.md","P07_Prozesse_und_Threads\\README.md","P07_Prozesse_und_Threads\\README_P02.md","P08_Sync\\README.md","P09_File_Operations\\README.md","P10_IPC\\README.md","index.rst"],objects:{},objnames:{},objtypes:{},terms:{"0":[0,1,2,3,6,7,8,9,10,11],"00":0,"000":[3,8,11],"0000":3,"0001":3,"0011":3,"00463":0,"00927":0,"01":[1,8,9,10,14],"0100":[3,5],"011":3,"0111":3,"01390":0,"01854":0,"02":[4,5,6,7,12,13,14],"02317":0,"02780":0,"02d":[1,2],"03":14,"03244":0,"03707":0,"04":14,"05":14,"06":14,"07":[5,14],"08":14,"09":14,"0b001":3,"0b010":3,"0b100":3,"0x01":3,"0x03":3,"1":[6,14],"10":[5,7,8,11,14],"100":[1,2,11],"1000":[0,11],"10000":1,"100er":1,"101":3,"11":[8,9,10],"110":3,"111":3,"12":[1,2,9],"1200":0,"127":[9,10],"128":11,"13":1,"14":[5,7],"1400":0,"15":[1,2,4,6],"1582":1,"1599":1,"16":[2,5,12,13],"1600":[0,1,2],"17":2,"1700":1,"18":11,"1st":8,"2":14,"20":[2,7,8],"200":0,"2013":2,"2017":2,"2019":2,"2020":1,"2021":11,"2022":[1,2,4,5,6,7,8,9,10,12,13],"2048":11,"21":5,"22":5,"2400":2,"266839126":5,"28":[1,2],"29":[1,2],"297":5,"2nd":8,"3":14,"30":[1,2,9],"31":[1,2],"32":3,"343302707":5,"36":5,"3rd":8,"3x3":[6,7],"4":[2,14],"40":8,"400":[0,1,2],"42":5,"43158":0,"47":0,"5":14,"6":[1,6,7,14],"600":0,"62":[6,7],"64":4,"65534":[9,10],"7":[1,3,4,14],"8":[3,14],"800":0,"9":[1,6,7,14],"9999":[1,2],"\u00e4hnlich":9,"\u00e4hnlichen":4,"\u00e4ltere":3,"\u00e4ndern":9,"\u00e4nderungen":4,"\u00f6ffentlichen":4,"\u00f6ffnen":[9,10,11],"\u00fcben":4,"\u00fcber":[3,4,6,7,9,10,11],"\u00fcberblick":11,"\u00fcberf\u00fchrt":3,"\u00fcberfl\u00fcssig":3,"\u00fcbergeben":[1,3],"\u00fcbergebenen":4,"\u00fcberlagerungen":3,"\u00fcberlassen":8,"\u00fcberlegen":[3,9,10],"\u00fcberlegt":9,"\u00fcberlegungen":[9,10],"\u00fcbernehmen":1,"\u00fcbernommen":[4,9,10,11],"\u00fcberpr\u00fcfen":[9,11],"\u00fcberpr\u00fcft":[1,8,11],"\u00fcberpr\u00fcfung":8,"\u00fcbersetz":4,"\u00fcbersetzen":[4,9,10,11],"\u00fcbersetzt":[0,4],"\u00fcbersetzung":4,"\u00fcbersicht":14,"\u00fcbersichtlich":9,"\u00fcbertragen":11,"\u00fcbertragungsfehl":3,"\u00fcberwachung":[9,10],"\u00fcberweisung":11,"\u00fcbrigen":[6,7],"\u00fcbungen":14,"abh\u00e4ngig":11,"abh\u00e4ngigkeit":[4,11],"abh\u00e4ngigkeiten":[4,6,7],"abh\u00e4ngigkeitsanalys":14,"abk\u00fcrzungen":1,"abl\u00e4uf":11,"abl\u00e4uft":[9,10],"abst\u00fcrzt":[9,10],"aktivit\u00e4ten":11,"allf\u00e4llig":0,"allgegenw\u00e4rtig":3,"aufh\u00e4lt":[9,10],"ausf\u00fchrbar":0,"ausf\u00fchrbaren":0,"ausf\u00fchren":[0,4,5,6,7,9,10],"ausf\u00fchrt":1,"ausf\u00fchrung":[9,10,11],"ausf\u00fchrungsschritt":11,"ausf\u00fchrungszeit":8,"ausgef\u00fchrt":[1,6,7,9,10,11],"ausgel\u00f6st":8,"ausgew\u00e4hlt":[9,11],"ben\u00f6tigen":[0,9,10,11],"ben\u00f6tigt":[9,10,11],"ben\u00f6tigten":[0,9,11],"ber\u00fccksichtigt":[1,8],"bereichs\u00fcberpr\u00fcfung":[1,3],"bereichspr\u00fcfung":1,"besch\u00e4ftigen":[9,10],"besch\u00e4ftigt":11,"betr\u00e4g":11,"betr\u00e4gen":[0,11],"bew\u00e4hrt":11,"boolean":4,"break":2,"byte":4,"case":[2,3,8],"char":[0,1,2,3,7,8],"class":4,"const":8,"d\u00e4mon":14,"d\u00e4monen":[9,10],"d\u00fcrfen":11,"daf\u00fcr":[1,9,10,11],"daten\u00fcbermittlung":3,"daten\u00fcbertragung":3,"daten\u00fcbertragungen":3,"default":5,"do":2,"dr\u00fccken":[9,10],"dr\u00fcckt":11,"druckerd\u00e4monen":[9,10],"durchf\u00fchren":[9,10],"eigenst\u00e4ndig":11,"einf\u00fcgen":8,"einf\u00fchrung":14,"eingef\u00fcgt":8,"eingef\u00fchrt":11,"eintr\u00e4g":[0,1,4],"eintr\u00e4gen":8,"einzuf\u00fcgend":8,"endger\u00e4ten":3,"enth\u00e4lt":[0,11],"enum":[2,14],"erf\u00fcllen":[1,11],"erf\u00fcllt":11,"erg\u00e4nzen":[1,4,5,7,8,11],"erg\u00e4nzend":[4,6,7],"erg\u00e4nzt":[1,3,8],"erg\u00e4nzungen":4,"erh\u00e4lt":[9,10],"erh\u00f6":11,"erh\u00f6hen":1,"erkl\u00e4ren":[1,3,4,5,6,7,8,9,10,11,12,13],"erkl\u00e4rt":[0,1,3,4,5,6,7,9,10,11,12,13],"erl\u00e4sst":11,"erm\u00f6glicht":1,"erw\u00e4hnt":[9,10],"f\u00e4llen":9,"f\u00e4llt":[9,10,11],"f\u00fcgen":[4,8],"f\u00fchren":[4,6,7],"f\u00fchrt":8,"f\u00fcr":[0,1,3,4,6,7,8,9,10,11,14],"fehlschl\u00e4gt":[6,7],"float":4,"function":5,"funktionalit\u00e4t":[4,6,7,11],"g\u00fclteigen":1,"g\u00fcltig":1,"g\u00fcltige":1,"g\u00fcltigkeit":1,"ge\u00f6ffnet":9,"gef\u00fchrt":[6,7],"gegen\u00fcb":[5,14],"geh\u00f6ren":3,"gel\u00e4ufigen":1,"gel\u00f6scht":[8,9,10],"gel\u00f6st":11,"geldbetr\u00e4g":11,"gem\u00e4ss":[4,6,7,8],"ger\u00fcst":[1,4,5,6,7,8],"getr\u00e4nk":11,"gew\u00e4hlt":[6,7,9,10,11],"gew\u00fcnsch":11,"gew\u00fcnschten":11,"gr\u00f6sser":[1,8],"gr\u00fcnde":11,"gr\u00fcnden":11,"grundverst\u00e4ndni":9,"herausgel\u00f6st":8,"herauszul\u00f6senden":8,"hinzuf\u00fcgen":14,"hinzugef\u00fcgt":8,"int":[1,2,3,4,6,7,8],"integrit\u00e4t":3,"k\u00f6nn":11,"k\u00f6nnen":[0,1,3,4,5,6,7,8,9,10,11,12,13],"k\u00f6nnte":[8,9,10,11],"k\u00f6nnten":9,"k\u00fcmmern":4,"k\u00fcrzerer":3,"k\u00fcrzungen":1,"komplexit\u00e4t":3,"l\u00e4ndern":11,"l\u00e4nge":[1,5,7],"l\u00e4sst":11,"l\u00e4uft":[9,10],"l\u00f6sbare":3,"l\u00f6schenden":8,"l\u00f6sen":11,"l\u00f6st":14,"l\u00f6sung":11,"l\u00f6sungen":[0,1,3,4,5,6,7,9,10,11,12,13],"l\u00f6sungsans\u00e4tz":0,"lauff\u00e4hig":[1,3],"long":[4,5],"m\u00e4chtige":4,"m\u00f6glich":[1,4,9,10,11],"m\u00f6glicherweis":1,"m\u00f6glichkeit":[5,9],"m\u00f6glichkeiten":4,"m\u00f6glichst":[3,11],"m\u00fcndlich":[0,1,3,4,5,6,7,9,10,11,12,13],"m\u00fcnze":11,"m\u00fcnzen":11,"m\u00fcssen":[0,1,3,4,5,6,7,8,9,10,11,12,13],"monatsl\u00e4ng":1,"n\u00e4chste":11,"n\u00e4chsten":[8,11],"n\u00e4mlich":1,"n\u00f6tig":8,"n\u00fctzlich":[3,9],"nat\u00fcrlich":7,"new":[4,5,6,7],"null":[8,9,10,11],"parit\u00e4tsbit":3,"popul\u00e4r":8,"pr\u00fcfen":[4,5,14],"pr\u00fcfend":8,"pr\u00fcfsummen":3,"pr\u00fcft":[1,6,7],"pr\u00fcfung":1,"pr\u00fcfungen":8,"prozessr\u00e4um":9,"public":4,"r\u00fcckg\u00e4ngig":3,"r\u00fcckgabewert":1,"repr\u00e4sent":4,"repr\u00e4sentiert":4,"return":[0,1,2,3,6,7,8],"s\u00e4mtlich":[9,10,11],"sch\u00fctzen":11,"selbst\u00e4ndig":0,"short":5,"sicherheitsgr\u00fcnden":11,"sp\u00e4ter":[1,4],"st\u00fccke":11,"static":[4,6,7],"switch":2,"tats\u00e4chlich":4,"tempor\u00e4ren":3,"throw":4,"true":4,"umst\u00e4nden":9,"umst\u00e4ndlich":8,"unabh\u00e4ngig":[9,10],"ung\u00fclti":1,"ung\u00fcltig":1,"ung\u00fcltigen":1,"unver\u00e4ndert":[1,6,7],"ver\u00e4ndert":11,"verf\u00fcgbar":[4,5,6,7,8],"verf\u00fcgung":[0,8,9,10,11],"verfollst\u00e4ndigen":3,"verh\u00e4ltniss":[9,10,11],"verkn\u00fcpfung":3,"verst\u00e4ndni":9,"void":[0,1,4,6,7],"vollst\u00e4ndig":[4,8,9,10,11],"vorg\u00e4ngerknoten":8,"w\u00e4hlen":[0,11],"w\u00e4hrend":[8,9,10],"w\u00e4re":[3,8,11],"w\u00f6rter":[7,14],"w\u00fcrde":4,"w\u00fcrden":11,"weiterf\u00fchrend":[1,9,10],"while":[0,2,3,9],"widerstandsf\u00e4hig":3,"wortzwischenr\u00e4um":0,"z\u00e4hlen":14,"z\u00e4hler":11,"z\u00e4hlt":0,"zerst\u00fcckelt":3,"zuf\u00e4llig":11,"zugef\u00fcgt":8,"zur\u00fcck":[0,1,3,9,10],"zur\u00fcckgeben":1,"zur\u00fcckgegeben":0,"zur\u00fcckgesetzt":11,"zur\u00fcckliefern":1,"zus\u00e4tzlich":[3,8,9],"zus\u00e4tzlichen":11,"zust\u00e4nd":9,A:[4,6,7,9,10],Be:11,IN:[6,7,8],In:[0,1,4,5,6,7,8,9,10,11],NO:5,TO:[6,7],The:[6,7],_:3,__file:4,__mbstate_t:4,_g_config:4,a1:11,a2:11,a3:11,a4tsbit:3,aa:1,aaaa:9,ab:[1,9,10,11],abbild:9,abbildung:[8,11],abbrechen:[9,10],aber:[3,8,9,10,11],abfragen:[1,9,10],abgebildet:8,abgefragt:9,abgelegt:[9,11],abgeschlossen:0,abgewandelt:[1,9,10,11],abheben:11,abhilf:11,ablauf:[11,14],ablaufbedingungen:11,ablaufen:[9,11],ablaufgraph:11,ablaufgraphen:11,ablegt:7,abnahm:[1,6,7],abort:9,abschnitt:[9,10,11],absend:[9,10],abspeichert:8,abstraktionen:9,absturz:9,abwechselnd:6,accept:[9,10],access:[6,7],account:11,achtung:11,acntlock:11,activ:4,adapt:2,add:[1,6,7],addit:[1,4],adress:[1,8],adressen:8,advancedsequ:11,affect:[6,7],ag:8,ajx:[9,10],aktion:8,aktionen:[9,10,11],aktiv:[9,10],aktiven:[9,10],aktuel:[9,10],aktuellen:[9,10],al:[0,1,2,3,4,5,6,7,8,10,11,14],all:[0,1,3,4,5,6,7,8,9,10,11],allen:4,aller:8,allozieren:8,alloziert:[8,11],allozierung:3,alphabetisch:[7,8],also:[1,8,9],alter:[2,8],alternativ:8,am:[1,4,5,6,8,9,10,11],ampel1:1,ampeln:1,an:[0,3,4,5,8,9,10,11,14],analog:[6,7],analys:[9,10],analysieren:[5,10,11,14],analysiert:11,anchor:8,ander:[3,4,8,9],anderem:11,anderen:[1,7,8,11],andererseit:[9,10],andern:4,andreren:1,anfang:[3,9,10],anforderungen:11,anfrag:[9,10],anfragen:[9,10],angaben:7,angebracht:8,angegeben:[1,4,5,6,8],angegebenen:4,angekommen:[9,10],angelehnt:1,angemessen:1,angenom:1,angenommen:[1,8],angepasst:[9,10,11],angezeigt:9,anhand:[5,6,7,8],anhang:[5,14],anker:8,anleitung:[0,6,7],anmerkung:[9,10],annahm:5,annehmen:11,anordnet:4,anordnung:4,anpassen:[9,10],anschli:11,anschliessend:11,ansonsten:[1,8],ansprechenden:4,anstel:[8,9,10],antwort:[9,10,11],anwend:[8,9,10],anwenden:[0,8,11],anwendung:3,anyoutput:9,anzahl:[0,1,2,11],anztag:2,anzugeben:4,anzupassen:7,anzuwenden:[1,4,5],api:14,apr:[1,2],apt:4,ar:8,arbeiten:[0,3,9,10,11],arbeitet:8,arbeitsverzeichni:[9,10],architektur:[6,7],arg:4,argc:[1,2],argument:[4,9,14],argumenten:1,argv:[1,2],arithmetik:1,arno:8,arrai:[1,6,8,11,14],art:[3,8,9,10],aspekt:1,assert:[1,6,7],assert_po:[6,7],asynchronen:3,atoi:[1,2],atomar:11,au:[0,1,2,3,4,5,6,7,8,9,10,11],auch:[1,3,4,6,7,8,9,10,11],auf:[0,4,6,7,8,10,11,14],aufdatiert:9,aufenthaltsort:[9,10],aufgab:[3,14],aufgaben:[8,10,14],aufgefordert:8,aufgelistet:9,aufgelisteten:8,aufgeru:1,aufgerufen:[1,6,7,8],auflisten:[9,10],auflistet:4,aufruf:[8,9,10],aufrufen:[0,1,8,11],aufrufendem:1,aufrufenden:1,aufruft:[1,9],aufsparen:9,auftreten:[5,11],aufweist:8,aufzeichnen:11,aufzurufen:1,aufzuteilen:[4,5],aug:[1,2],ausdrucken:[9,10],ausgab:[0,1,2,3,4,5,7,9,10,11],ausgaben:0,ausgeben:[0,1,7,9,11],ausgegeben:[1,4,8,9,11],ausgehend:[8,9],ausgibt:[0,1,7,9,11],auskommentiert:11,auskommt:3,auslastung:9,ausloggen:[9,10],ausnahm:1,ausnahmen:1,aussag:11,ausschluss:11,ausser:[9,11],aussieht:9,auswahl:[8,9],auswahlen:3,auszug:3,auszugeben:0,author:2,auto:11,automat:14,automaten:11,automatisch:[4,9,10],automatisierten:4,autor:11,autoren:[9,10],b:[0,1,3,4,5,6,7,8,9,10,11],bank:14,bash:[4,6,9],basi:[4,9,10,11,14],basicsequ:11,basierend:8,basiert:1,basierten:11,baum:9,bazz:2,bb:1,bcfsumm:3,bcsselung:3,beachten:[1,8,9],bearbeiten:11,bearbeitung:8,bedarf:[0,1],bedeutet:[1,8],bedingungen:[1,8],beeinflussen:11,beendet:8,beendigung:7,befehl:[9,10],begin:[1,6,7],beginnen:0,begrenzten:3,behandelt:[9,10],behandlung:1,bei:[0,1,3,4,6,7,8,9,10,11],beid:[3,5],beiden:[6,7,8,9,10,11],beider:1,beim:[0,8,9,10,11],beinhalten:1,beispiel:[1,3,4,9,10,11],bekannt:[8,9,10],beliebig:[8,9,10],beliebigen:[0,1],benennen:9,benutz:8,benutzen:[0,1,8],benutzt:[6,7,11],beobachten:9,beobachtungen:[9,10],berechnen:1,berechnet:1,berechnung:[2,3,5,14],bereich:[1,2],bereit:[4,7,11],besagt:[4,6,7],beschreiben:11,beschreibung:[4,5,6,7,8,14],beschreibungen:[9,10,11],beschrieben:[4,9],besetzt:6,besitzen:[9,10],besitzt:[1,11],besond:3,besser:[1,11],bestandteil:1,bestehen:[5,8],bestehend:[2,4,7,14],bestehenden:[5,8,9],besteht:[1,4,6,7,8,9,10],besten:1,bestimmen:[2,14],betrag:11,betrieb:[9,10],betriebssystem:[0,9,10],bevor:0,bewertung:14,bewirkt:9,bezeichneten:1,beziehen:11,beziehung:11,beziehungen:11,bi:[0,1,2,3,6,7,8,9,11],bibliothek:11,bieten:8,bietet:[4,9],bild:1,bilden:1,bildet:8,bildschirm:[9,10],bin:[3,4,6,7,9,10],bisher:8,bit:[4,14],bitcoin:0,bitfeld:3,bitweis:3,black:[3,4],bleibt:8,block:3,blue:3,board:[6,7],bodi:4,boilerpl:2,bosshard:8,box:4,branchlock:11,brett:[6,7],bricht:[9,10],brief:[6,7,8],bsy:[9,10,11],btc:0,buchen:11,buchstaben:[8,9],buffer:4,buffers:4,bug:9,burkert:2,bzw:[3,9,10,11],c3:3,c:[1,3,6,7,9,10,11,14],ca8_2:9,ca:[9,10],calculate_weekdai:1,call_graph:5,caller_graph:5,carl:1,cat:9,cc:1,cd:11,cdata:11,cdef:4,cess:[9,10],charact:0,chef:11,chf:0,childproca2:9,childproca7:9,chmod:9,clean:[4,5],clear:[3,8],cluster_c0:4,cluster_c1:4,code:[0,1,2,6,7,8,9,11,14],codezeil:1,codierung:9,coffe:11,coffeetel:11,coin:11,coincount:11,col:[4,6,7],color:[3,4,5],com:[1,9],command:[1,4,9],committ:11,common:[4,11],commondef:11,commun:9,compar:8,comparison:8,compil:[0,4,5],comput:3,condit:11,content:8,control:[6,7],convers:0,convert:3,copyright:1,core:9,count:11,cpu:[9,11],cpu_meltdown:9,cpuinfo:9,critic:14,ctrl:[9,10],cu_assert_:8,cu_assert_equal_fat:[6,7],cunit:8,current:3,custom:11,cyan:3,d:[1,2,3,4,5,7,8,9,10,11],da:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],dabei:[3,6,7,8,11],daemon:[9,10],daemonen:[9,10],daemonisierung:[9,10],daemonprogramm:[9,10],dai:1,damit:[1,3,5,9,10,11],danach:[0,6,7,9],dann:[0,1,4,8,9,10,11],dar:[1,6,7,9],darau:9,darauf:1,darf:[1,2,8,9,10,11],dargestellt:[4,9],darin:[6,7],darstel:4,darstellen:[3,4],darstellt:[6,7],darstellung:[3,4,6,7,9],darstellungen:5,darum:5,darzustellen:[3,5,9],dass:[1,3,4,5,6,7,8,9,10,11],data:4,date:1,date_t:1,datei:[0,4,6,7,9,10],dateien:[5,9,10],dateierzeugungsmask:[9,10],dateizugriffsrecht:[9,10],daten:[0,1,3,4,8,9,11],datenflusssteuerung:3,datenkommunik:[9,10],datenkompress:3,datennetz:3,datensatz:8,datenstruktur:[8,11],datenstrukturen:8,datenstukturen:5,datentyp:14,datentypen:1,datenwert:11,datum:14,dauernd:11,davon:1,dazu:[1,3,4,8,9,10,11],dd:1,ddd:1,de:[0,3,4,5,6,7,8,10,11,14],deadlock:11,dealloziert:11,debugg:9,dec:[1,3],deep:4,defin:[0,2,3,4,8],definieren:[0,1,4,5,8,11],definierend:1,definiert:[1,3,4,8,11],definiertem:3,definierten:3,definit:[1,3],definitionen:[5,8],defunct:9,deklarationen:5,deklarieren:[1,4,5,8],dem:[0,1,2,3,5,6,7,8,9,10,11],demonstrieren:[1,3,4,6,7,8,12,13],demontiert:[9,10],den:[0,1,3,4,5,6,7,8,9,10,11],denen:[0,3],denjenigen:9,denn:8,dep2dot:4,depend:4,depfil:4,deposit:11,der:[0,1,2,3,4,5,6,7,8,9,10,12,13,14],deren:[1,4,8,11],derselb:8,derselben:3,deshalb:[4,8,9,10],designvorgaben:14,desktop:3,dessen:[1,3,8],detail:[1,8,9],develop:7,dez:[1,2],dezemb:1,di:[0,3,4,6,7,8,9],diagonalen:6,diagramm:5,die:[0,2,3,4,5,6,7,8,9,10,11,12,13,14],dienen:3,dient:4,dies:[0,1,2,4,5,6,7,8,11,14],dieselb:4,diesem:[0,1,4,5,6,7,8,9,10,11],diesen:7,dieser:[4,8,9,10,11],diesmal:11,digraph:4,dir:4,directory_graph:5,direkt:[1,7,8,9,10],diskutieren:[0,9],displai:5,disziplinen:3,doc:5,dokument:4,dokumentieren:11,dollar:11,donnerstag:1,doppelt:8,dort:[9,10],dot_image_format:5,dot_path:5,dotfile_dir:5,doubl:[0,4,5],doxyfil:14,dport:[9,10],drei:[1,3,4,11],dreieck:4,dritt:14,dritten:11,driven:7,drucker:[9,10],dump:9,duplikat:8,durch:[1,3,4,6,7,8,9,11],durchlau:6,durchlaufen:[4,6,7,9],dynamisch:[8,9],dynamischen:8,e:[8,9,10,11],each:4,ebenfal:[1,11],echo:9,editor:0,effizient:11,effizienten:7,effizientest:3,eien:1,eigen:11,eigenen:[7,9,11],eigenschaft:[9,10],eigenschaften:9,eigentlich:[6,7,9,10],ein:[0,1,2,3,4,5,6,7,8,9,10,11],einbinden:[0,4,14],einblick:9,einbuchen:11,eindeutig:1,eindeutigen:1,einem:[0,1,3,4,5,6,7,8,9,10,11],einen:[0,1,2,6,7,8,9,10,11],einer:[0,1,3,5,6,7,8,9,10,11],einfach:[0,3,4,7,8,9,14],einfachen:11,einfachheit:[9,10],eingab:[0,1,2,3,4,6,7,8,11],eingaben:8,eingabeschleif:8,eingeben:[9,10],eingebunden:5,eingegeben:[1,7,8,11],eingegebenen:0,eingehalten:11,eingehen:[9,10],einheiten:11,einig:[0,9,10],einleitung:[9,10],einlesen:[1,2,7],einliest:[0,7],einloggen:[9,10],einmal:[8,9,10],einstiegspunkt:8,eintreffen:[9,10],einwerfen:11,einzeln:[0,9,11],einzelnen:[3,4,5,8,11],einzig:[4,8],einzu:8,einzugeben:8,einzulesen:0,element:[8,14],elementen:1,els:[0,2],eltern:9,elternprozess:[10,14],emov:8,end:[2,3,4,5,6,7,8],endet:8,endlo:11,endung:[0,4],englisch:[9,10],englischen:1,ent:1,enter:0,entfernen:[3,8,9,10],entfernt:8,entgegeben:1,entgegen:1,entgegennimmt:1,enthalten:9,entscheiden:4,entsprechend:[4,8,9,10,11],entsprechenden:[4,5,8,9,10,11],entspricht:7,entstehen:[8,11],entstehend:9,entwed:[0,8,11],entwickeln:7,entwicklungsteam:11,eof:4,equal:[6,7],er:[9,10,11,14],erbt:[9,10],erfahren:9,erfinden:7,erfolgen:[3,7],erfolgreich:[1,4,6,7,8],erfolgreichen:1,erforderlich:11,erfragen:9,erhalten:9,erkannt:[1,3],erkennbar:8,erkennen:8,erkennung:8,erledigen:[9,10],ermitteln:11,erneut:[9,10],erreichen:[7,8],erreicht:[8,9,10],error:[4,9],error_in_month:2,error_in_year:2,erscheint:11,ersetzen:14,ersetzt:4,ersichtlich:[9,10],erst:[1,3,4,6,7,8,9,14],erstellen:[0,4,5,11],erstellt:5,erstem:9,ersten:[0,1,4,5,6,7,11],erwarten:9,erwartet:8,erweitern:[1,4,5,6,7,8,11],erweitert:[5,14],erweiterten:11,erweiterung:14,erzeugen:[5,14],erzeugt:[0,8,9,10],erzeugten:9,erzeugung:9,erzwingen:11,erzwingt:9,erzwungen:11,es:[1,3,4,5,6,7,8,9,10,11],etc:[1,9,10,11],etlich:8,etwa:3,euro:11,ev:[5,9,10],exclus:14,exec:14,execl:9,existieren:[6,7,8],existiert:8,exit:[1,9],exit_failur:1,exit_success:[1,3],exklusiv:11,experimenti:[9,10],experimentieren:9,express:3,extra:3,f:9,fail:[6,7],fall:[1,8,9,10,11],fals:4,falsch:[3,9],featur:4,feb:[1,2],februar:[1,2],fehelcod:2,fehleingaben:1,fehlenden:[1,3,6,7],fehler:[1,3,5,6,7,8],fehlerbehandlung:8,fehlerfal:8,fehlerhaft:8,fehlermeldung:[9,10,11],fehlermeldungen:11,fehlschlagen:[6,7],feld:[6,7],felder:[3,6],fen:[1,6],fenster:[9,10],ferner:7,fest:[9,10,11],festgestellt:11,feststellen:[9,10],festzustellen:9,fget:[1,2],field:[6,7,8],file:[0,6,7,8,9,10,11,14],filepath:[9,10],filial:11,filialen:14,fill:4,finden:[0,1,3,9,10,11],firefox:[4,5],firewal:[9,10],first:[6,7],first_nam:8,floatn:4,foku:1,fol:1,folgen:0,folgend:[1,3,4,6,7,8,9,10],folgendem:[8,9,10],folgenden:[1,4,6,7,8,9,11],folgt:[6,7],fordern:11,forderung:11,fork:14,form:[3,4],format:14,formaten:3,formatiert:[0,4],formatierten:1,formatstr:1,formel:1,fprintf:[1,4],fragen:[9,10,11],framework:0,franken:0,frei:11,freigab:11,freigaben:11,freigeben:8,freigegeben:[8,9,10],friedrich:1,fuer:2,funktion:[0,1,3,6,7,9,11],funktionen:[0,4,5,6,7,8,9,11,14],funktionieren:[6,7],funktionierend:[0,1,3,4,5,6,7,8,9,10,11,12,13],funktionierenden:[6,7],funktionsaufruf:9,funktionsdefinit:1,funktionsdeklar:1,funktionsdeklarationen:8,g:4,ganz:5,gauss:1,gcc:[0,4],gdb:9,geben:[1,8,9,11],gebildet:4,gedacht:[9,10],geeignet:7,gefolgt:9,gefordert:[6,7],geforderten:1,gefunden:8,gegeben:[1,4,6,7],gegebenen:[1,4,8,9],gegebenenfal:[0,1,3,4,5,6,8,9,10,11,12,13],gegen:3,gegensatz:11,gegenseitig:11,gehen:[6,7,8,11],geht:5,gelb:1,geld:11,gelder:11,gelernt:11,gelernten:8,gelesen:[0,9],gelesenen:[1,4],gelingt:9,gelten:4,gemacht:9,gemeinsam:11,gemeinsamen:11,gemeldet:8,gemounteten:[9,10],genannt:[8,9,10,11],genau:8,gend:1,generieren:4,generierten:4,georg:1,ger:1,gerrit:2,gesamt:8,gesamtbilanz:11,gesamten:[8,9],geschehen:[9,10],geschickt:9,geschieht:[9,10],geschrieben:[4,9],gesetzt:[9,10,11],gesorgt:[9,10],gespeichert:[1,3,8],gespeicherten:8,gespielt:[6,7],gestartet:[1,9,10,11],gestellt:11,gestellten:[0,1,3,4,5,6,9,10,11,12,13],gesteuert:[3,11],gesucht:8,get_length:5,get_month_length:1,get_slop:5,get_stat:[6,7],getchar:0,getint:4,gewicht:[1,3,6,11],gewinn:[6,7],gewinnt:6,gewissen:9,gezeigt:[0,1,3,4,5,6,7,9,10,11,12,13],gibintwert:[1,2,3],gibt:[0,1,2,3,4,9,10,11],gif:4,git:[4,5,6,7,8],given:[6,7],glaeser:1,gleich:[0,1,8,9,11],gleichen:[9,10],gleichzeitig:[9,10,11],gnu:[0,4,9],grad:3,grafik:3,grafikprotokollen:3,grafisch:4,graphen:4,graphical_hierarchi:5,graphik:4,graphisch:4,graphischen:[4,5],graphviz:4,greater:8,green:3,gregorianisch:1,gregorianischen:1,gregorianischer_kalend:1,greifen:11,grep:[9,10],gross:11,grossbuchstaben:[4,7],grossteil:4,gruen:1,grund:[0,7],grundlagen:[3,11],grundlegend:11,grundlegenden:11,guard:[4,5,8],gui:3,gute:[8,9],h:[0,1,2,3,4,5,6,7,8,9,10,11],haben:[0,4,9,10,11],halber:[9,10],halbtot:14,hallo:[9,10],hand:9,handel:11,handelt:8,handgriff:[4,5],hard_work:9,hat:[1,2,4,6,7,9,10],hauptprogram:9,hauptprogramm:[1,8,9,10],hauptschleif:8,header:[4,8,14],heap:8,heben:11,hello:14,hen:11,henfolg:8,her:[8,11],herausfordern:6,herum:8,herunterfahren:[9,10],heruntergefahren:[9,10],hervorhebung:3,hex:3,hier:[1,2,3,4,5,6,7,9,10,11],hierbei:7,hilf:[5,11],hilfetext:1,hilfreich:1,hilfsfunkt:1,hilfsfunktionen:[1,8,9,10],hin:[0,11],hingegen:11,hintereinand:[9,11],hintergrundprozess:[9,10],hinterlegt:[9,10],hinwei:[9,10,11],hinweis:[7,14],hinzu:8,hold:[6,7],home:5,horizontalen:6,how:[4,8],html:5,htop:9,http:[1,3,4,9,11],huno:5,i:[8,9,10],ibc:11,ibt:11,ich:[9,10,11],id:[0,9,10],ide:8,identifizieren:11,ignorieren:[9,10],ignoriert:[7,8],ihnen:[0,8,9,10,11],ihr:[0,9,10,11],ihrem:[0,9,10,11],ihren:[0,9,10],ihrer:[9,10],im:[0,1,4,5,6,7,8,9,10,11],imag:14,immer:[1,3,11],impl:11,implemen:1,implement:[4,6,7,14],implementationen:11,implementationsfil:8,implementieren:[1,4,6,7,8,11],implementiert:[6,7,9,10,11],implementierung:[1,14],inact:[6,7],inclu:4,includ:[0,1,2,3,4,5,7,8],include_graph:5,included_by_graph:5,indem:[0,3,4,8,9,10],indent:4,index:5,individuel:8,ineffizi:8,info:[9,10],inform:[3,9],informationen:[3,9,10],informationssystemen:3,inhalt:[9,10],init_model:[6,7],initialisiert:11,initialisierung:11,inklus:[1,3,4,6,7,8,12,13],inkrementel:4,inkrementiert:11,innerhalb:11,inout:[6,7],input:[0,4,5,9,10],ins:[4,11],insert:8,inspir:4,instal:4,installationsanleitung:0,installieren:0,installiert:4,instanc:[6,7],instruct:[6,7],intakt:8,integ:1,integriert:4,inter:9,interactive_svg:5,interakt:[9,10],interess:3,interessiert:1,intern:[6,7,14],io:4,ioexcept:4,ip:[9,10],ipc:[9,14],iptabl:[9,10],irgendein:11,is_gregorian_d:1,is_leap_year:1,is_valid_d:1,isoliert:8,ist:[0,1,2,3,4,5,6,7,8,9,10,11],istschaltjahr:[1,2,3],itlb_multihit:9,j:[9,10],ja:[1,9,10],jahr:[1,2,8],jahreszahl:1,jahrhundert:1,jan:[1,2],januar:1,java:4,javac:4,je:[3,4,8],jede:[1,3,11],jeden:[1,9,11],jeder:[8,11],jederzeit:[9,10,11],jedoch:[1,11],jemanden:6,jene:4,jeweil:[7,8],jeweiligen:[9,11],jpg:11,jul:[1,2],jun:[1,2],justwork:9,kaffe:14,kaffeekauf:11,kaffeesort:11,kaffeewahlen:11,kaffeewahltasten:11,kalend:1,kann:[1,3,4,6,7,8,9,10,11],kanten:4,kauf:11,kehrt:0,kein:[1,2,5,8,9,10,11],kennen:[4,7,8,9,11],kennenlernen:[9,10],kennt:4,kett:8,kill:9,killal:[9,10],kind:9,kindern:9,kindprogramm:9,kindprozess:14,kindprozessen:14,klein:[0,1,5],kleiner:[1,3,8],knoten:[4,8],kolleg:11,kollegen:[9,10,11],kombin:1,kommando:1,kommandozeil:0,kommandozeilen:[9,10],kommen:[4,9],kommentar:[1,9,10],kommentieren:9,kommt:[3,4,9,11],kommunik:[3,9,10],kommunizieren:[9,10],kompil:[4,5],kompilieren:[0,5],kompiliert:[4,5],kompilierzeit:8,komplett:[0,8],kompletten:0,komplex:4,komplexer:[3,11],komponenten:[9,10],komprimiert:4,konfigurationsdateien:5,konsist:11,konsistent:11,konsolen:[9,10],konstant:2,konstanten:1,konstrukt:3,kontakt:[9,10],konten:11,kontenzugriff:11,kontenzugriffen:11,kontext:1,kontinuierlich:3,konto:14,kontostand:11,kontrollstrukturen:0,kontrolltermin:[9,10],konzentrieren:[6,7],konzept:3,koordin:5,koordinaten:5,koordinatensystem:5,koordiniert:11,kopi:14,kopieren:11,korrekt:[3,4,5,11,14],korrekten:11,korrektheit:14,korrigieren:[6,7],korrigiert:3,kostet:11,kostspielig:3,krei:8,kreieren:[8,9,10],kriterium:[1,3,4,6,7,8,9,10,11,12,13],kryptographi:3,kund:11,kunden:11,kur:0,kurz:[9,10],l1tf:9,l:5,lab:[4,5,6,7,8],label:4,lang:8,langsamen:3,lassen:3,lauf:9,laufen:[6,7,8],laufzeit:[8,11],laufzeiten:11,leak:8,lear:8,lebt:[9,10],lediglich:[9,10],leer:[0,8],leerzeichen:0,legen:6,lehnen:4,leicht:[9,10,11],leiten:9,len:1,lernen:[1,4,5,6,7,9,11],lernziel:14,lesbar:1,lesbarkeit:1,lesen:[0,1,3,9,10],letzt:[1,8],letzten:1,lib:4,libc:4,libio:4,librari:[0,7,8],liegen:[4,11],liest:[2,4,6,7,11],lightgrei:4,line:[1,4,5,9],lini:[5,6],linien:5,link:14,linux:[0,4,9],list:[4,9,11,14],list_clear:8,list_insert:8,list_remov:8,listen:4,load:9,localhost:[9,10],lock:[9,10,11],locken:11,log:[9,10],logik:[6,7],lokal:11,lokalen:11,loop:0,lower:14,lowercas:3,lpd:[9,10],lscpu:9,lung:4,lust:[9,10],m:[1,5,9,10,11],machen:[0,1,8],macht:[3,4,9,10],macro:[0,9,10],magenta:3,mai:[1,2,4],mail:[9,10],main:[1,2,3,4,6,7,14],make:[1,4,5,6,7,8,9,10,11],makefil:[6,7,8,14],makro:[0,8],mal:[3,9,10],man:[1,3,4,8,9,10,14],manipul:3,manipulationen:3,mann:9,manual:9,mar:[1,2],markieren:3,maschin:14,mat:11,math:5,max:8,max_numb:4,maximalen:7,maximum:11,md:9,mehr:[1,4,6,7,11],mehrer:[0,3,4,5,11],mehreren:[1,9,11],mehrmal:[9,11],mehrmalig:11,meint:11,meist:[9,10],meisten:3,meldet:11,memori:8,men:1,mentiert:11,messen:[9,10],methoden:4,mind:5,mindesten:[8,9,10],mit:[1,2,3,4,5,6,7,8,10,11,14],miteinand:0,mittel:[1,3,4,5],mix:3,mm:1,model:[6,7],model_can_mov:[6,7],model_get_st:[6,7],model_get_winn:[6,7],model_init:[6,7],model_pos_t:[6,7],model_state_non:[6,7],model_state_t:[6,7],model_t:[6,7],modelliert:11,modul:[4,8,9,10,11,14],modular:4,modularisieren:14,modularisierung:14,modulen:[4,8],mon:1,monat:[2,14],monatsnumm:1,monatswert:1,montag:1,month:1,month_t:1,montierten:[9,10],moodl:0,mrtimedaemon:[9,10],mscfile_dir:5,mtop:9,mueller:8,multipl:4,muss:[1,2,3,4,5,7,8,9,10,11],mutex:11,mutual:14,mv:9,mvc:[6,7],my:[6,7],mytabl:[9,10],n:[0,1,2,3,6,7,9],nach:[1,3,4,7,8,9,10,11],nachdem:5,nachfolgeknoten:8,nachfolgenden:0,nachschaut:[9,10],nachwei:14,nahe:4,name:[2,8],name_len:8,namen:[0,1,4,8,9,10,11],nd:8,nein:1,nend:1,nennen:9,neu:[8,11],neue:[9,10,11,14],neuen:8,neuer:8,neuzeichnungen:3,newlin:0,next:8,nicht:[0,1,2,3,4,6,7,8,9,10,11],nimmt:1,noch:[3,9,10,11],nochmal:9,node:[4,8],node_t:8,normal:[4,9,10],normalen:[9,10],normalfal:[9,10],noti:11,notieren:[9,10,11],notizen:9,notwendigen:[5,9,10,11],nov:[1,2],nsert:8,num_coin:11,num_row:0,number:3,numerisch:9,nummer:[1,9,10],nun:[5,9,10,11],nur:[1,9,10,11],nutzen:[5,11],o:[0,1,8],ob:[1,4,8,9,10,11],oben:[1,8,9],obergrenz:1,obig:[1,6,7],obigen:[1,4,6,7,8],object:5,oct:2,oder:[0,1,2,3,6,7,8,9,10,11],offensichtlich:3,offiziel:4,oft:[4,11],ohn:[1,4,6,7,8,9,10,14],okt:[1,2],oktob:1,onen:11,oper:[1,2,3,8,14],operand:3,operand_1:3,operand_2:3,operanden:3,operati:11,operationen:[8,11,14],operiert:7,option:[0,4,10,14],optionen:9,orchest:9,ordnung:8,org:[1,3,4,11],orphan:14,ort:9,orten:11,other:4,out:[0,1,4,9,10],output:[0,4,9,10,11],p1:8,p2:8,p:[9,10],page:[4,9],papier:11,paradigma:[6,7],param:[6,7,8],paramet:1,parametern:8,parit:3,parsen:14,pass:[6,7],passen:[4,5,11],passenden:[4,5],passiert:[9,10],periodisch:[9,10],perror:9,person:14,person_compar:8,person_t:8,personen:14,personenangaben:8,personeneingaben:8,personenverwaltung:14,pfade:[9,10],pfeilen:11,phonei:4,phoni:5,phoren:11,pid:[9,10],pidi:[9,10],planet:1,plappermaul:[9,10],plu:[5,8,9,10],png:5,po:[6,7],point:5,pointer:8,pointern:8,posit:[3,6,7],positiv:5,posix:11,potentiel:[8,11],potenz:14,power:3,pr:3,praktika:[0,5],praktikum:[0,1,4,5,6,7,8,9,10,11],praktikumsbetreuung:[0,1,3,4,5,6,7,9,10,11,12,13],praktikumsumgebung:0,preis:11,previou:8,primari:3,print:4,print_weekdai:1,printer:[9,10],printf:[0,1,2,3],println:4,privilegien:[9,10],pro:[2,9,10,11,14],probieren:11,problem:[3,4,11],problematisch:[9,10],problemstellung:[9,10,11],problemstellungen:[9,10],proc:9,proca1:9,proca2:9,proca3:9,proca4:9,proca5:9,proca6:9,proca7:9,proca8_1:9,proca8_2:9,proca8_3:9,proca9:9,process:14,procthread:[9,10],produc:8,programm:[0,1,2,3,4,5,6,7,8,10,11,12,13,14],programmausgab:9,programmcod:[0,1,3,4,5,6,7,9,10,11,12,13],programmen:1,programmfunkt:14,programmi:1,programmieraufgab:0,programmieraufgaben:0,programmierfehl:0,programmierung:[0,1,3,4],programmrahmen:[7,14],prozedur:9,prozess:[11,14],prozessen:[11,14],prozesshierarchi:14,prozesshierarchien:9,prozessraum:9,pruefen:2,ps:[9,10],pseudo:4,pseudocod:11,pstree:9,pthread:[9,11],pthread_exit:9,pthread_join:9,puffer:0,puffert:0,punkt:[4,5,7,8,9,10,12,13],pusher:11,pushern:11,putpid:[9,10],qualit:11,quell:[1,9,11],quit:9,r:8,race:11,rahmen:[6,8,9,10],ran:[6,7],rang:1,rank:4,raster:4,rate:0,raten:11,re:1,re_v2:9,read:4,readint:4,readm:9,reagieren:1,rechner:[0,9,10,14],rechnung:3,rechtwinklig:4,record:8,rect:4,rectang:4,rectangular:4,red:3,redundant:3,refactor:14,refer:8,regardless:3,regel:[1,4,5],regeln:[1,14],rei:8,reicht:11,reih:[7,11],reihenfolg:[1,9,14],remark:8,remot:3,remov:8,ren:[4,11],replac:[6,7],repositori:[4,5,6,7,8],ressourcen:[3,11],restor:[9,10],result:[8,11],resultat:[3,4,9,11],resultaten:11,richtig:9,richtlinien:11,root:[9,10],rot:1,routin:9,row:[6,7],run:[6,7],s:[1,2,8,9],same:[4,8],sammlung:5,sampl:4,sat:1,sauber:0,save:[9,10],scan:[6,7],scanf:[0,3],schaffen:3,schafft:11,schaljahr:1,schalten:9,schaltjahr:[1,2],schaltjahrberechnung:1,scheint:3,schickt:[9,10],schlecht:1,schleif:[0,8,9,10,11],schliessen:[9,10,11],schliesslich:[1,4,5,6,7],schltjahr:1,schluss:[7,9,10,11],schnell:11,schon:[4,8,9,10],schreiben:[0,1,4,7,8,9,10,14],schreibt:[0,1,9,10],schriftlich:11,schritt:[4,5,9,10,11,14],schritten:11,schrittweis:[6,7],schrumpfen:8,script:[5,9],section:14,segment:9,sehen:9,sehr:8,sein:[0,1,2,3,8,9,10,11],seiner:[9,10],sekunden:[9,10],selber:[8,9],selbst:[0,7,8,9],selcount1:11,selcount2:11,selectcpu:9,sem_open:11,sem_post:11,sem_wait:11,sema:11,semaphor:11,semaphoren:11,send:11,sendet:9,sep:[1,2],sequenc:8,seriellen:3,server:[9,10],set:[3,4,6,7],set_stat:[6,7],setcpu:9,setzen:[0,9,10,11],sh:5,shape:4,share:5,shell:[4,9,10],show:[4,8],sich:[3,4,6,7,8,9,10,11],sicher:[1,11],sichert:[9,10],sicherung:1,sicherzustellen:[9,10],sicht:11,sie:[0,1,3,4,5,6,7,8,9,10,11,12,13],sieh:[1,4,6,8,9,11],sieht:8,sigabrt:9,sigint:[9,10],signal:[9,10],signatur:1,simuliert:9,sind:[0,1,3,4,5,6,7,8,9,10,11],sinn:11,sinnvol:[1,9,10],situationen:8,skript:11,sleep:9,snp:[4,5,7,8,9,10,11],so:[0,1,3,4,5,8,9,11],socket:[9,10],softwareinterrupt:[9,10],sogenannt:11,solang:8,solch:4,solchen:[4,8],soll:[0,1,4,7,8,9,10,11],sollen:[1,4,5,7,8,9],sollt:[3,4,9,10],sollten:[5,7,9,10],solut:3,somit:[3,6,7],sondern:[0,9,11],sonntag:1,sonst:7,sortieralgorithmu:7,sortieren:14,sortiert:[7,8,9],sortierten:[7,8],sourc:[0,4,5,11],sourcecod:0,sowi:[0,1],sowohl:11,spalt:[9,11],spec_store_bypass:9,spect:9,spectre_v1:9,speicher:[3,8,9],speichern:0,speichernden:8,speichert:8,speicherung:7,speichervariablen:11,speicherzugriffsverletzung:9,sperrt:[9,10],speziel:[1,9,10,11],spezifisch:8,spiel:[6,7],spielbrett:[6,7],spieler:[6,7],spielern:[6,7],spielzug:[6,7],sport:[9,10],sprach:14,spricht:1,sqrt:5,src:[4,5,6,7,8],sreedev:3,sscanf:1,stack:4,standard:[0,4,7,8],standardausgab:0,start:[3,4,9,10],startapp:11,starten:[9,10,11],startet:[9,10,11],state:[6,7],statisch:8,statischen:8,stdarg:4,stddef:4,stderr:[1,4],stdin:[0,2,4,6,7],stdio:[0,1,2,3,4],stdio_lim:4,stdlib:[2,3,4],stdout:[1,4],stehen:[1,6,7,9,10],steht:8,steigen:11,steigung:5,stein:6,stel:1,stell:0,stellen:[0,1,4,8,9,10,11],stellig:1,stelligen:1,stellt:[1,6,7,9,11],steuert:[6,7],steuerung:9,stichwortartig:11,stimmen:[9,10],stimmt:9,stirbt:[9,10],stoppbit:3,stoppen:[9,10],stoppt:11,stream:[1,4],string:[3,4,8,14],strncmp:8,struct:[1,5,8,14],struktur:8,strukturen:[0,8,11],strukturieren:[4,5,7],strukturiert:1,stub:[4,6,7],student:[2,6,7],studieren:[9,10],studierend:5,style:4,subgraph:4,suchen:[4,6,7],sudo:4,suedbahn:11,suffix:4,suit:[4,6,7],summari:[6,7],sun:1,svg:5,swapg:9,switchanweisung:1,sy:4,symbol:4,sync:11,synchron:3,synchronis:14,synchronisationsbedingung:11,synchronisationsbedingungen:11,synchronisationsoperationen:11,synchronisationsproblem:14,synchronisationsproblemen:11,synchronisieren:11,synchronisiert:11,sys_errlist:4,system:[4,9,10],systematisch:11,systemen:3,systemfunktionen:[9,10],systemkomponenten:[9,10],systemnah:0,systemzeit:[9,10],t:0,tab2svg:5,tabel:0,tabellenausgab:14,tabulatoren:0,tac:[6,7],tag:1,tage:[2,14],tagen:[1,2],tagepromonat:[1,2,3],target:4,tast:[9,10],tastatur:[0,7],tatsach:3,tauschen:[11,14],tcp:[9,10],tdd:[6,7],teil:[1,3,4,9,10,11],teilaufgab:[6,14],teilaufgaben:[1,6,7,8],teilbar:1,teilt:[9,10],temp:3,template_rel:5,ten:11,termin:[6,7],terminieren:[0,1,6,7,10,14],terminiert:[0,1,10,14],terminierung:9,ternaeren:[1,2],test:[1,2,3,4,5,7,12,13,14],test_model_can_mov:14,test_model_get_st:14,test_model_get_win_lin:14,test_model_get_winn:14,test_model_init:14,test_model_mov:14,testen:[8,9,10,11],teurer:11,text:[1,4,11],textuel:4,thaler:[9,10,11],themen:[1,3],theorieaufgaben:[0,1,3,4,5,6,9,10,11,12,13],thi:8,thread:[10,11,14],tic:[6,7],tictacto:14,tierten:1,timedaemon:[9,10],timeserv:[9,10],tipp:1,tippen:9,tmp:[9,10],todo:[6,7],toe:[6,7],toggl:3,token:3,tool:4,top:9,total:[6,7],tpng:4,tragen:11,transaktionen:11,transfer:11,transferiert:11,triangl:4,two:8,txt:[9,10],typ:[8,9,10],type:[4,6,7],typedef:[1,8,14],typen:[1,4,5,8,11],types:4,typisch:[9,10],typt:1,ubuntu:0,ueberpruefen:[1,2],ui:9,um:[0,1,3,4,5,7,8,9,10,11],umfang:9,umgebung:[9,10],umgehen:7,umgesetzt:[5,6,7,8],uml_limit_num_field:5,uml_look:5,umrechnungsfaktor:0,umsetzen:[0,7],umsetzt:1,umsetzung:[4,5,14],umwandelt:7,umwelt:[9,10],umzuleiten:[9,10],umzuwandeln:0,un:[1,9],unbedingt:[0,1],unbefugt:3,und:[2,3,5,6,10,11,12,13,14],unendlich:9,unendlichen:[9,10],unfd:1,unit:[6,7,14],unix:[9,10],unlock:11,unser:1,unsign:[3,8],unten:[3,4,6,7,8],unter:[0,1,8,9,10,11],unterschi:14,unterschied:[4,9],unterschiedlich:3,unterschiedlichen:[5,7,9],uppercas:14,us:[4,8],usag:1,usr:4,v:4,vagrant:5,valu:8,variabl:[1,4,5,11,14],variablen:[1,8,11,14],variablennam:1,variant:[1,3,9,10,11],velgast:11,verarbeiten:8,verarbeitungsreihenfolg:11,vererbt:9,verfahren:3,verfolgen:9,vergleich:11,vergleichen:[7,9,11],verhalten:[8,9,11],verhindern:[9,10],verhindert:11,verifizieren:9,verkauft:11,verketten:8,verkettet:8,verketteten:8,verkettung:8,verlangen:[9,10],verlassen:9,verletzung:9,verliert:11,verlust:3,verlustfrei:4,vermeiden:3,vermieden:3,vermittl:[6,7],verschachtelungstief:4,verschieden:[0,4,9,11],verschiedenen:[3,8,11],verschl:3,version:[1,2,4,6,7,8,9,10,11,12,13],verstehen:9,versucht:3,vertauschen:3,vertieften:9,vertikalen:6,verwaisten:9,verwaltung:14,verwenden:[0,1,3,7,8,9,11],verwendet:[1,2,9,11],verwendeten:[3,4,9],verworfen:8,verzeichni:[4,9,10,11],verzeichniss:[9,10],verzichten:11,verzweigungen:0,via:[1,9],viel:[0,4,8,11],vielen:3,vielzahl:9,vier:[1,8,11],view:[6,7],virtuel:14,visualisierung:4,vm:11,vollem:[6,7],vom:[2,4,8,9,10],von:[0,1,2,3,6,10,11,14],vor:[1,6,7,9,10,11],vorbei:[6,7],vorbemerkung:9,vorbereitet:11,vorbereiteten:0,vordefiniert:11,vordefinierten:11,vordergrundprozess:[9,10],vorgab:[4,5],vorgaben:[6,7,11,14],vorgeben:5,vorgegeben:1,vorgegebenen:[4,5,6,7,8,11],vorgehen:11,vorgehensweis:[6,7],vorhanden:[1,4,8],vorkommen:8,vorlag:0,vorlesung:9,vorlesungen:7,vorlesungsfolien:0,vorliegenden:11,vorraussetzung:5,vorschlag:8,vorteil:[4,11],wa:[1,4,6,7,9,10,11],wachsen:8,wahl:[6,7,8,9,10,11],wahr:3,waisenkind:14,wait:9,waitpid:9,wall:0,wann:11,waren:9,warnungen:0,warten:[11,14],wartet:[9,10,11],web:4,wechseln:[9,10,11],wechselt:[6,7],weekdai:1,weekday_t:1,weil:[3,9,10,11],weist:0,weiter:[3,4,8,9,10],weiteren:[0,8],weitergehend:8,welch:[0,1,4,5,6,7,8,9,11],welchen:11,welcher:[1,6,7],wenden:[0,8],wenn:[1,4,6,7,8,9,10,11],wer:9,werden:[0,1,2,3,4,5,6,7,8,9,10,11,12,13],werkzeug:9,wert:[1,2,9,11],werten:11,wesentlichen:[8,11],wesentlichst:1,whatsthetimemr:[9,10],where:9,which:[6,7],white:3,wichtig:11,wichtigsten:3,wie:[0,1,3,4,5,6,7,8,9,10,14],wieder:[0,3,8,9,10,11],wiederherstellen:[9,10],wiederholt:7,wiederum:[9,10,11],wieso:[9,11],wiki:[1,3,11],wikimedia:11,wikipedia:[1,3],wikiwand:9,wir:[0,8,9,10,11],wird:[0,1,3,4,5,6,7,8,9,10,11],wirft:11,wirklich:9,wissen:[1,4,5,9,11],wissenschaften:3,withdraw:11,wo:[1,6,8,9,10,11],wobei:[1,4,6,7,11],wochentag:14,wochentagsberechnung:1,wohl:11,wollen:[9,10],word:[3,4],wordptr:3,wordsiz:4,work:5,workerutil:9,world:14,wovon:8,wurd:[1,8],wurden:[9,10,11],wurzel:8,www:[1,4,9],x1:5,x2:5,x86_64:4,x:[5,9],xkcd:1,xor:3,y1:5,y2:5,y:[1,5],ye:5,year:1,yellow:3,yyyi:1,z:[0,1,3,4,7,8,9,10,11],zahl:8,zahlen:1,zehn:7,zeichen:[1,14],zeichnen:[5,9,11],zeigen:8,zeiger:[9,10],zeigt:[8,9],zeil:[0,1],zeilen:[1,4],zeilenumbruch:1,zeit:[9,10,11],zeitanfrag:[9,10],zeitintervallen:9,zeitlich:[11,14],zeitlichen:11,zeitpunkt:9,zeman:[9,10],zentral:0,zess:11,ziel:[9,10],ziffer:[6,7],ziffern:[6,7],zombi:14,zu:[0,1,3,4,5,6,7,8,11,14],zudem:11,zuerst:[6,7,9,10,11],zugegriffen:[6,11],zugeschrieben:1,zugewiesen:8,zugreifen:[7,9,10,11],zugriff:[6,7,9,14],zugriffsrecht:[9,10],zugriffsverletzung:9,zum:[1,2,4,7,9,10,11],zumindest:[3,9,10],zur:[0,4,5,6,7,8,9,10,11],zusammenfassung:14,zusammengefasst:8,zusatz:[9,10],zusatzinform:14,zusatzinformationen:14,zusatzvari:3,zustand:[3,9],zuweisen:8,zwar:11,zwecken:4,zwei:[1,3,6,8,9,10,11],zweidimensional:7,zweiergruppen:0,zweit:[1,3,9,10],zweiten:[4,5,7,11],zwischen:[0,2,6,7,9,10,11],zwischenspeich:3,zyklisch:8,zzz:7},titles:["01 - Erste Schritte mit C","02: Funktionen, Datentyp \u201cenum\u201d","L\u00f6sungsskizzen","03 - Bit Operationen, Struct, Typedef","04 - Modularisieren von C Code","04 - Modularisieren von C Code","05 - SNP: TicTacToe","05 - Arrays/Strings/TicTacToe","06 - Personen Verwaltung \u2013 Linked List","07 - Prozesse und Threads","09/02 - D\u00e4mon Prozesse","08 - Synchronisationsprobleme","09 - File Operations","10 - IPC","SNP Laboratories"],titleterms:{"01":0,"02":[1,10],"03":3,"04":[4,5],"05":[6,7],"06":8,"07":9,"08":11,"09":[10,12],"1":[0,1,2,3,4,5,7,8,9,10,11,12,13],"10":[9,13],"2":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"3":[0,1,3,4,5,6,7,8,9,10,11,12,13],"4":[0,1,3,4,5,6,7,8,9,10,11,12,13],"5":[0,1,4,5,6,7,8,9,10,11],"6":[0,4,5,8,9,11],"7":[0,5,8,9],"8":9,"9":9,"\u00fcbersicht":[0,1,4,5,6,7,8,9,10,11,12,13],"\u00fcbungen":3,"abh\u00e4ngigkeitsanalys":5,"d\u00e4mon":[9,10],"einf\u00fchrung":11,"enum":1,"f\u00fcr":5,"gegen\u00fcb":9,"hinzuf\u00fcgen":[4,5],"l\u00f6st":11,"l\u00f6sungsskizzen":2,"pr\u00fcfen":[1,3],"w\u00f6rter":0,"z\u00e4hlen":0,"zus\u00e4tzlich":4,ablauf:9,al:9,an:1,analysieren:9,anhang:[1,4],api:8,argument:1,arrai:7,auf:[1,3,9],aufgab:[0,1,2,4,5,6,7,8,9,10,11,12,13],aufgaben:[1,9,11],automat:11,bank:11,basi:3,berechnung:1,beschreibung:1,bestehend:8,bestimmen:1,bewertung:[0,1,3,4,5,6,7,8,9,10,11,12,13],bit:3,c:[0,4,5,8],code:[4,5],content:6,critic:11,datentyp:1,datum:1,de:[1,9],dep:4,der:11,designvorgaben:8,develop:6,die:1,dies:[9,10],dot:4,doxyfil:5,dritt:3,driven:6,einbinden:5,einfach:11,element:[1,4],elternprozess:9,er:3,ersetzen:9,erst:0,erweitert:11,erweiterung:5,erzeugen:9,exclus:11,exec:9,file:[4,5,12],file_format:4,filialen:11,fork:9,format:[1,4],funktionen:1,halbtot:9,header:5,hello:0,hinweis:1,imag:9,implement:[8,9,10,11],implementierung:8,inhalt:4,intern:11,ipc:13,kaffe:11,kindprozess:9,kindprozessen:9,konto:11,kopi:9,korrekt:1,korrektheit:1,laboratori:14,lernziel:[0,1,4,5,6,7,8,9,10,11,12,13],link:8,list:8,lower:3,main:8,makefil:[4,5],man:11,maschin:0,mit:[0,9],modul:5,modularisieren:[4,5],modularisierung:8,monat:1,mutual:11,nachwei:[9,10,11],name:4,neue:[4,5],ohn:3,oper:12,operationen:3,option:9,orphan:9,parsen:1,person:8,personen:8,personenverwaltung:8,png:4,potenz:3,pro:1,process:9,programm:9,programmfunkt:8,programmrahmen:8,prozess:[9,10],prozessen:[9,10],prozesshierarchi:9,rechner:3,refactor:11,regeln:[4,5],reihenfolg:11,schreiben:5,schritt:0,section:11,snp:[6,14],sortieren:7,sprach:[1,4],string:7,struct:3,synchronis:11,synchronisationsproblem:11,tabellenausgab:0,tage:1,tauschen:3,teilaufgab:[1,5,7,8],terminieren:9,terminiert:9,test:[6,8],test_model_can_mov:[6,7],test_model_get_st:[6,7],test_model_get_win_lin:[6,7],test_model_get_winn:[6,7],test_model_init:[6,7],test_model_mov:[6,7],thread:9,tictacto:[6,7],tipp:4,typedef:3,umsetzung:1,und:[0,1,4,7,8,9],unit:8,unterschi:9,uppercas:3,variabl:3,variablen:3,verarbeitung:4,verwaltung:8,verwendet:4,virtuel:0,von:[4,5,7,8,9],vorgaben:1,waisenkind:9,warten:9,wie:11,wochentag:1,world:0,zeichen:0,zeitlich:9,zombi:9,zu:[9,10],zugriff:11,zusammenfassung:11,zusatzinform:[9,10],zusatzinformationen:[9,10]}}) \ No newline at end of file diff --git a/build/latex/main.pdf b/build/latex/main.pdf new file mode 100644 index 0000000..0c5f300 Binary files /dev/null and b/build/latex/main.pdf differ diff --git a/conf.py b/conf.py new file mode 100644 index 0000000..dc9f3e5 --- /dev/null +++ b/conf.py @@ -0,0 +1,300 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('')) + + +# -- Project information ----------------------------------------------------- + +project = 'SNP Labs' +copyright = '2022, stsh' +author = 'stsh' +master_doc = 'index' + +# The full version, including alpha/beta/rc tags +release = '' + +#LaTex + + + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'myst_parser', # pip install myst-parser + 'sphinx.ext.duration', + 'sphinx.ext.doctest', + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + #'sphinxcontrib.mermaid',# pip install sphinxcontrib-mermaid + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'sphinx.ext.githubpages' +] + +myst_enable_extensions = [ + "amsmath", + "colon_fence", + "deflist", + "dollarmath", + "fieldlist", + "html_admonition", + "html_image", + "linkify", + "replacements", + "smartquotes", + "strikethrough", + "substitution", + "tasklist", +] + +# -- Options for LaTeX output --------------------------------------------- +latex_engine = 'pdflatex' + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + 'papersize': 'a4paper', + 'releasename':" ", + # Sonny, Lenny, Glenn, Conny, Rejne, Bjarne and Bjornstrup + # 'fncychap': '\\usepackage[Lenny]{fncychap}', + 'fncychap': '\\usepackage{fncychap}', + 'fontpkg': '\\usepackage{amsmath,amsfonts,amssymb,amsthm}', + 'figure_align':'htbp', + # The font size ('10pt', '11pt' or '12pt'). + # + 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + 'preamble': r''' + %% %% %% %% %% %% %% %% %% %% Meher %% %% %% %% %% %% %% %% %% + %% %add number to subsubsection 2=subsection, 3=subsubsection + %% % below subsubsection is not good idea. + \setcounter{secnumdepth}{3} + % + %% %% Table of content upto 2=subsection, 3=subsubsection + \setcounter{tocdepth}{2} + \usepackage{amsmath,amsfonts,amssymb,amsthm} + \usepackage{graphicx} + %% % r educe spaces for Table of contents, figures and tables + %% % i t is used "\addtocontents{toc}{\vskip -1.2cm}" etc. in the document + \usepackage[notlot,nottoc,notlof]{} + \usepackage{color} + \usepackage{transparent} + \usepackage{eso-pic} + \usepackage{lipsum} + \usepackage{footnotebackref} %% link at the footnote to go to the place of footnote in the text + %% spacing between line + \usepackage{setspace} + %% %% \onehalfspacing + %% %% \doublespacing + \singlespacing + %% %% %% %% %% % d atetime + \usepackage{datetime} + \newdateformat{MonthYearFormat}{% + \monthname[\THEMONTH], \THEYEAR} + %% RO, LE will not work for 'oneside' layout. + %% Change oneside to twoside in document class + \usepackage{fancyhdr} + \pagestyle{fancy} + \fancyhf{} + %% % Alternating Header for oneside + \fancyhead[L]{\ifthenelse{\isodd{\value{page}}}{ \small \nouppercase{\leftmark} }{}} + \fancyhead[R]{\ifthenelse{\isodd{\value{page}}}{}{ \small \nouppercase{\rightmark} }} + %% % Alternating Header for two side + %\fancyhead[RO]{\small \nouppercase{\rightmark}} + %\fancyhead[LE]{\small \nouppercase{\leftmark}} + %% for oneside: change footer at right side. If you want to use Left and right then use same as header defined above. + %% %\fancyfoot[R]{\ifthenelse{\isodd{\value{page}}}{{\tiny } }{\href{http://pythondsp.readthedocs.io/en/latest/pythondsp/toc.html}{\tiny PythonDSP}}} + %% % Alternating Footer for two side + %\fancyfoot[RO, RE]{\scriptsize ()} + %% % page number + \fancyfoot[CO, CE]{\thepage} + \renewcommand{\headrulewidth}{0.5pt} + \renewcommand{\footrulewidth}{0.5pt} + + \RequirePackage{tocbibind} %% % c omment this to remove page number for following + \addto\captionsenglish{\renewcommand{\contentsname}{Table of contents}} + \addto\captionsenglish{\renewcommand{\listfigurename}{List of figures}} + \addto\captionsenglish{\renewcommand{\listtablename}{List of tables}} + % \addto\captionsenglish{\renewcommand{\chaptername}{Chapter}} + %% reduce spacing for itemize + \usepackage{enumitem} + \setlist{nosep} + %% %% %% %% %% % Quote Styles at the top of chapter + \usepackage{epigraph} + \setlength{\epigraphwidth}{0.8\columnwidth} + \newcommand{\chapterquote}[2]{\epigraphhead[60]{\epigraph{\textit{#1}}{\textbf {\textit{--#2}}}}} + %% %% %% %% %% % Quote for all places except Chapter + \newcommand{\sectionquote}[2]{{\quote{\textit{``#1''}}{\textbf {\textit{--#2}}}}} + ''', + + 'maketitle': r''' + \pagenumbering{Roman} %% % to avoid page 1 conflict with actual page 1 + \begin{titlepage} + \centering + \vspace*{40mm} %% % * is used to give space from top + \textbf{\Huge {SNP Laboratories}} + \vspace{0mm} + \begin{figure}[!h] + \centering + \includegraphics[scale=0.5]{en-zhaw-ines-rgb.png} + \end{figure} + \begin{figure}[!h] + \centering + \textbf{ welo, bazz, fisa, huno, grop, donn, scia } + \end{figure} + \vspace*{20mm} + \vspace{20mm} + + \vspace*{0mm} + \small Last updated : \MonthYearFormat\today + %% \vfill adds at the bottom + \vfill + \small \textit{More documents are available at }{\href{https://github.zhaw.ch/SNP/snp-lab-code}{github.zhaw.ch}} + \end{titlepage} + \clearpage + \pagenumbering{roman} + \tableofcontents + %% %\listoffigures + %% %\listoftables + \clearpage + \pagenumbering{arabic} + ''', + + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', + 'sphinxsetup': \ + 'hmargin={0.7in,0.7in}, vmargin={1in,1in}, \ + verbatimwithframe=true, \ + TitleColor={rgb}{0,0,0}, \ + HeaderFamily=\\rmfamily\\bfseries, \ + InnerLinkColor={rgb}{0,0,1}, \ + OuterLinkColor={rgb}{0,0,1}', + 'tableofcontents':' ', +} + +latex_logo = 'en-zhaw-ines-rgb.png' +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'main.tex', 'SNP Laboratories', + '', 'report') +] + + +# --------------------------------------------------- + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +source_suffix = { + '.rst': 'restructuredtext', + '.md': 'markdown', +} + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# These patterns also affect html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +highlight_options = { + 'default': {'stripall': True}, + 'php': {'startinline': True}, +} + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' +html_theme_path = ['_static'] + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +html_theme_options = { + 'logo': 'logo.png', + 'logo_name' : 'true', +} + +language = "en" +myst_html_meta = { + "description lang=en": "metadata description", + "description lang=fr": "description des métadonnées", + "keywords": "Sphinx, MyST", + "property=og:locale": "en_US" +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..124836a --- /dev/null +++ b/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/index.rst b/index.rst new file mode 100644 index 0000000..749ecfe --- /dev/null +++ b/index.rst @@ -0,0 +1,26 @@ +.. demo documentation master file, created by + sphinx-quickstart on Wed Feb 9 08:17:44 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + +SNP Laboratories +---------------------------------- + +.. toctree:: + P01_Erste_Schritte_mit_C/README.md + P02_Funktionen_Datentyp_enum/README.md + P03_Bit_Operation_struct_typedef/README.md + P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.md + P05_TicTacToe/README.md + P06_Personen_Verwaltung_Linked_List/README.md + P07_Prozesse_und_Threads/README.md + P08_Sync/README.md + P09_File_Operations/README.md + P10_IPC/README.md + \ No newline at end of file diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..307157b --- /dev/null +++ b/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd