commit ded01301c2366152a5ea086cebfe42b9180155d3 Author: Marc Date: Sat Sep 13 14:40:16 2025 +0200 Initial Commit diff --git a/OU1/changes.txt b/OU1/changes.txt new file mode 100644 index 0000000..079711e --- /dev/null +++ b/OU1/changes.txt @@ -0,0 +1,10 @@ +Changes 2025-05-01 + Cleanup of uneccesary comments + Improved function comments + Added space between functions + Fixed value_equal to specifications + added exit() to solve broken tests + added check of stack to fix test 8 and test 10 + Checked code again for memory leaks, no leaks found on my + machine (latest fedora). Also tested on scratchy with older GCC. + Updated report small code changes. \ No newline at end of file diff --git a/OU1/compile.sh b/OU1/compile.sh new file mode 100644 index 0000000..5ab1698 --- /dev/null +++ b/OU1/compile.sh @@ -0,0 +1,3 @@ +#!bin/bash +gcc -o stack_test -I /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/include/ stack_test.c /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/src/stack/stack.c && gcc -o int_stack_test -I /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/include/ int_stack_test.c /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack.c + diff --git a/OU1/datastructures-v2.2.2.2/.gitignore b/OU1/datastructures-v2.2.2.2/.gitignore new file mode 100644 index 0000000..ddf1912 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/.gitignore @@ -0,0 +1,13 @@ +# Vim swap files. +*.swp +*.swo +# Object files. +*.o +# gcc precompiled headers. +*.gch +# Compiled programs for testing. +*_mwe +*_mwe[0-9] +*_mwe[0-9]i +*_test +*_test[0-9] diff --git a/OU1/datastructures-v2.2.2.2/CODING_STYLE.md b/OU1/datastructures-v2.2.2.2/CODING_STYLE.md new file mode 100644 index 0000000..6bba949 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/CODING_STYLE.md @@ -0,0 +1,98 @@ +# Kodkonvention + +I detta dokument beskrivs den kodkonvention som har följts/ska följas +för kod som läggs till kodbasen. Konventionen är till stor del baserad +på kodkonventionen som följs i Linux-kärnan. Därför kommer läsaren +hänvisas till dokumentet med den officiella kernelkonventionen. + + +# Linuxkärnans kodkonvention + +Se [Linux kernel coding style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html). +Nedan följer en uppräkning av vilka punkter vi ska följa, samt +eventuella avvikelser från dessa. Om en punkt från dokumentet ovan ej +nämns nedan så gäller punkten ej. + +## 1. Indentation + +Indenteringskonventionen ska följas helt och hållet. Det är möjligt att +konfigurera utvecklingsmiljön (e.g. Codelite) att automatiskt indentera +korrekt. + +## 2. Breaking long lines and strings + +Denna punkt är också mycket viktig, samt något din utvecklingsmiljö kan +hantera eller varna för. + +## 3. Placing Braces and Spaces + +Här ska alla punkter följas (även de under 3.1), med ett undantag: +Ensamma satser i t.ex. if-satser *ska* ha måsvingar. + +## 4. Naming + +När det kommer till namngivning är det viktigt att ha i åtanke att koden +tillsammans med kommentarerna ska tydligt förklara syftet med koden. Därför är +det viktigt att konventionen som beskrivs under `4. Naming` tolkas med detta i +hänsyn. Det innebär till exempel att om koden blir enklare att läsa med +variabelnamnet `counter` istället för `c` eller `tmp`, bör `counter` användas +istället. + +## 5. Typedefs + +Här avviker kursen från Linuxkärnans kodkonvention. För att främja +datatypernas gränsytor och göra dem så lik de teoretiska gränsytorna +som möjligt så används `typedefs` för t.ex. `struct list_pos`. I +Linuxkärnan hade motsvarande implementation krävt att det skrevs +`struct list_pos pos` överallt, medan datatyperna givna på kursen +förenklar detta till `list_pos pos`. Det viktiga här är att lära sig +gränsytorna. + +## 6. Functions + +Denna konvention ska följas så som den presenteras. Långa funktioner kommer +under denna kurs alltid vara onödigt, dela upp i mindre funktioner! Det ökar +läsbarheten, förenklar debugging, samt gör det enklare att diskutera koden. + +## 7. Centralized exiting of functions + +Det finns anledningar till att `goto` används i Linuxkärnan (e.g. +bakåtkompabilitet), men under denna kurs ska ni ej använda detta. + +## 8. Commenting + +Följ det första exemplet för kommentarer. Det finns exempel i de givna +datatyperna på hur funktioner kan dokumenteras. + +## 14. Allocating memory + +I Linuxkärnan finns inte `malloc` och `calloc`, motsvarande funktioner heter +`kmalloc` samt `kcalloc`. Vi kommer under denna kurs att översätta Linuxkärnans +konvention till följande: + +```c +p = malloc(sizeof(*p)); +``` + +samt + +```c +p = calloc(n, sizeof(*p)); +``` + +Det är fullt möjligt att i det senare fallet vända på parametrarna (i.e. +`calloc(sizeof(*p), n)`) då storleken på minnet som allokeras beräknas som +första argumentet gånger det andra argumentet (i.e., +`n*sizeof(*p) == sizeof(*p)*n`). + +## 16. Function return values and names + +Här är det första och sista stycket vad som är viktigt att följa. Om en funktion +returnerar huruvida funktionen lyckades eller ej så signifierar `0` ett lyckat +anrop, och negativa värden signifierar fel (e.g. `-1` om en fil inte finns, `-2` +om filen inte gick att läsa). Om en funktion returnerar en pekare `p`, så +indikerar `p == NULL` att funktionen inte lyckades. + +# Avslutning + +Det finns visst rum för tolkning under några av dessa punkter. diff --git a/OU1/datastructures-v2.2.2.2/ChangeLog.txt b/OU1/datastructures-v2.2.2.2/ChangeLog.txt new file mode 100644 index 0000000..b5d3842 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/ChangeLog.txt @@ -0,0 +1,114 @@ +Summary changelog file for release. + +Release 2.2.2.2, Jan 24, 2025. +- Added int_queue_example_internal. +- Added symmetric examples to queue. + +Release 2.2.2.1, Jan 16, 2025. +- Added int_queue_example without any queue_print. + +Release 2.2.2.0, Jan 10, 2025. +- Added int_queue. +- Added release date to the code base. + +Release 2.2.1.5, May 15, 2024. +- Fixed array_1d merge confusion. + +Release 2.2.1.4, May 15, 2024. +- Whitespace/doc fix. + +Release 2.2.1.3, May 15, 2024. +- Bugfix to remove sprintf warning. + +Release 2.2.1.2, May 10, 2024. +- Untabified all code. + +Release 2.2.1.1, May 10, 2024. +- Bugfix to sprintf overflow warning. + +Release 2.2.1.0, May 10, 2024. +- Added typedef free_function for backward compatibility with pre-v2.0 code. + +Release 2.2.0.0, May 10, 2024. +- Added *_print_internal for all datatypes. +- Updated several *_print_internal functions to improve visualization of encapsulation. +- Added master Makefile to src. + +Release 2.1.0.0, Apr 15, 2024. +- Added *_print_internal for table, array_1d, array_2d and int_array_1d, int_stack. +- Updated comments in table.h to allow for different internal handling of duplicates. + +Release 2.0.0.1, Apr 05, 2024. +- Bugfix of one MWE in array_1d that never should have been changed. + +Release 2.0.0.0, Apr 04, 2024. +- Major upgrade with functions to print the internal memory structure + of the generic data types in graphviz/dot format. +- Added internal_print functions for list, dlist, queue, and stack. +- Added mweNi for list, dlist, queue, and stack. +- Renamed free_function to kill_function to reduce confusion with free() function. + +Release 1.0.14.3, Jan 16, 2024. +- Cleanup of some #include directives that used citation marks. + +Release 1.0.14.2, Mar 23, 2023. +- Cleanup of empty statements in some list test programs. + +Release 1.0.14.1, Mar 23, 2023. +- Renamed all list_pos_are_equal to list_pos_is_equal. + +Release 1.0.14.0, Mar 21, 2023. +- Cleaned up list_test programs. +- Renamed list header fields to head and tail. + +Release 1.0.13.0, Jan 21, 2023. +- Added two test programs to list and one to int_list_array. + +Release 1.0.12.0, Jan 21, 2023. +- Renamed list_pos_equal to list_pos_are_equal. +- Added int_list_test program. + +Release 1.0.11.0, Jan 19, 2023. +- Added list_pos_equal and list_pos_is_valid to list types. + +Release 1.0.10.0, Jan 14, 2023. +- Added version information to lib + all MWEs. + +Release 1.0.9, Mar 24, 2022. +- Added int_stack. + +Release 1.0.8, Mar 06, 2019. +- Added table_choose_key(). + +Release 1.0.7, Mar 04, 2019. +- Bugfix in table_remove(). +- Minor improvement of array_2d_print(). + +Release 1.0.6, Feb 21, 2019. +- Added table version without dlist/memhandler. +- Added Makefiles for each data type. +- Updated various README.md files. + +Release 1.0.5, Jan 23, 2019. +- Cleaned up list_test.c: No cleanup on failure, improved messages and + proper file comment. + +Release 1.0.4, Apr 03, 2018. +- Split MWE to one case per file. +- Modified list_test to NOT use memhandler. + +Release 1.0.3, Feb 08, 2018. +- Added table implementation. + +Release 1.0.2, Jan 29, 2018. +- Removed table from public area. +- Cleaned up links, tabbing of many files. +- Added list_test test file. +- Added ChangeLog (this file). +- Updated README.md + +Release 1.0.1, Jan 29, 2018. +- Removed array from public area. + +Release 1.0.0, Jan 28, 2018. +- First public release. diff --git a/OU1/datastructures-v2.2.2.2/README.md b/OU1/datastructures-v2.2.2.2/README.md new file mode 100644 index 0000000..cecfc92 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/README.md @@ -0,0 +1,114 @@ +# Datatypsimplementationer för kursen datastrukturer och algoritmer vid Institutionen för datavetenskap, Umeå universitet. + +# Version 2.2.2.2, 2025-01-24. + +# Givna datatyper + +Här återfinns implementationer av några av de datatyper som kommer användas +under denna kurs. Nedan följer information om hur dessa kompileras och hur +`minimal working examples` körs för varje datatyp. + +## Headerfiler + +Headerfilerna för de givna datatyperna är alla samlade i mappen `include`. Dessa +kan inkluderas med hjälp av `-I`-flaggan till `gcc`, se exempel nedan. + +## undefined reference + +Det är möjligt att du får t.ex. följande felmeddelande: + +```bash +/tmp/ccLd0e5H.o: In function `queue_empty': +queue.c:(.text+0x14): undefined reference to `list_empty' +``` + +Detta innebär att du inte anget alla nödvändiga C-filer till kompilatorn. I +fallet ovan saknas `list.c`. + +# En-dimensionellt fält + +```bash +user@host:~$ cd ~/datastructures/src/array_1d +user@host:~/datastructures/src/array_1d$ gcc -std=c99 -Wall -I../../include/ array_1d.c array_1d_mwe1.c -o array_1d_mwe1 +user@host:~/datastructures/src$ ./array_1d_mwe1 +[ [1], [4], [9], [16], [25], [36] ] +user@host:~/datastructures/src/array_1d$ gcc -std=c99 -Wall -I../../include/ array_1d.c array_1d_mwe2.c -o array_1d_mwe2 +user@host:~/datastructures/src$ ./array_1d_mwe2 +[ [(Jan, 31)], [(Feb, 28)], [(Mar, 31)], [(Apr, 30)], [(May, 31)], [(Jun, 30)], [(Jul, 31)], [(Aug, 31)], [(Sep, 30)], [(Oct, 31)], [(Nov, 30)], [(Dec, 31)] ] +``` +# Två-dimensionellt fält + +```bash +user@host:~$ cd ~/datastructures/src/array_2d +user@host:~/datastructures/src/array_2d$ gcc -std=c99 -Wall -I../../include/ array_2d.c array_2d_mwe1.c -o array_2d_mwe1 +user@host:~/datastructures/src/array_2d$ ./array_2d_mwe1 +[ [ [11], [12], [13] [ [21], [22], [23] [ [31], [32], [33] [ [41], [42], [43] ] +``` +# Lista (oriktad, dubbellänkad) + +```bash +user@host:~$ cd ~/datastructures/src/list +user@host:~/datastructures/src/list$ gcc -std=c99 -Wall -I../../include/ list.c list_mwe1.c -o list_mwe1 +user@host:~/datastructures/src/list$ ./list_mwe1 +List after inserting one value: +( [5] ) +List after inserting second value at the end: +( [5], [8] ) +List after inserting a third value in the middle: +( [5], [2], [8] ) +List after removing first element: +( [2], [8] ) +``` + +# Riktad lista (enkellänkad) + +```bash +user@host:~$ cd ~/datastructures/src/dlist +user@host:~/datastructures/src/dlist$ gcc -std=c99 -Wall -I../../include/ dlist.c dlist_mwe1.c -o dlist_mwe1 +user@host:~/datastructures/src/dlist$ ./dlist_mwe1 +("Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar") +``` + +# Kö + +```bash +user@host:~$ cd ~/datastructures/src/queue +user@host:~/datastructures/src/queue$ gcc -std=c99 -Wall -I../../include/ queue.c queue_mwe1.c ../list/list.c -o queue_mwe1 +user@host:~/datastructures/src/queue$ ./queue_mwe1 +QUEUE before dequeuing: +{ [1], [2], [3] } +QUEUE after dequeuing: +{ [2], [3] } +``` + + +# Stack + +```bash +user@host:~$ cd ~/datastructures/src/stack +user@host:~/datastructures/src/stack$ gcc -std=c99 -Wall -I../../include/ stack.c stack_mwe1.c -o stack_mwe1 +user@host:~/datastructures/src/stack$ ./stack_mwe1 +--STACK before popping-- +{ [3], [2], [1] } +--STACK after popping-- +{ [2], [1] } +``` + +# Tabell + +```bash +user@host:~$ cd ~/datastructures/src/table +user@host:~/datastructures/src/table$ gcc -std=c99 -Wall -I../../include/ table.c table_mwe1.c ../dlist/dlist.c -o table_mwe1 +user@host:~/datastructures/src/table$ ./table_mwe1 +Table after inserting 3 pairs: +[98185, Kiruna] +[90184, Umea] +[90187, Umea] +Lookup of postal code 90187: Umea. +Table after adding a duplicate: +[90187, Umea (Universitet)] +[98185, Kiruna] +[90184, Umea] +[90187, Umea] +Lookup of postal code 90187: Umea (Universitet). +``` diff --git a/OU1/datastructures-v2.2.2.2/include/array_1d.h b/OU1/datastructures-v2.2.2.2/include/array_1d.h new file mode 100644 index 0000000..95faae1 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/array_1d.h @@ -0,0 +1,150 @@ +#ifndef __ARRAY_1D_H +#define __ARRAY_1D_H + +#include +#include "util.h" + +/* + * Declaration of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The array stores void pointers, so it can be used to + * store all types of values. After use, the function array_kill must + * be called to de-allocate the dynamic memory used by the array + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the array, + * unless a kill_function is registered in array_create. + * + * An element value of NULL is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct array_1d array_1d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_1d *array_1d_create(int lo, int hi, kill_function kill_func); + +/** + * array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int array_1d_low(const array_1d *a); + +/** + * array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int array_1d_high(const array_1d *a); + +/** + * array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_1d_inspect_value(const array_1d *a, int i); + +/** + * array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_1d_has_value(const array_1d *a, int i); + +/** + * array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_1d_set_value(array_1d *a, void *v, int i); + +/** + * array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_1d_kill(array_1d *a); + +/** + * array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_1d_print(const array_1d * l, inspect_callback print_func); + +/** + * array_1d_print_internal() - Print the internal structure of the array in dot format. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/array_2d.h b/OU1/datastructures-v2.2.2.2/include/array_2d.h new file mode 100644 index 0000000..3020ece --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/array_2d.h @@ -0,0 +1,158 @@ +#ifndef __ARRAY_2D_H +#define __ARRAY_2D_H + +#include +#include "util.h" + +/* + * Declaration of a generic 2D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The array stores void pointers, so it can be used to + * store all types of values. After use, the function array_kill must + * be called to de-allocate the dynamic memory used by the array + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the array, + * unless a kill_function is registered in array_create. + * + * An element value of NULL is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Moved freehandler to last in create parameter list. + * v1.2 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct array_2d array_2d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * array_2d_create() - Create an array without values. + * @lo1: low index limit for first dimension. + * @hi1: high index limit for first dimension. + * @lo2: low index limit for second dimension. + * @hi2: high index limit for second dimension. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2, + kill_function kill_func); + +/** + * array_2d_low() - Return the low index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The low index limit for dimension number d. + */ +int array_2d_low(const array_2d *a, int d); + +/** + * array_2d_high() - Return the high index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The high index limit for dimension number d. + */ +int array_2d_high(const array_2d *a, int d); + +/** + * array_2d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_2d_inspect_value(const array_2d *a, int i, int j); + +/** + * array_2d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_2d_has_value(const array_2d *a, int i, int j); + +/** + * array_2d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_2d_set_value(array_2d *a, void *v, int i, int j); + +/** + * array_2d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_2d_kill(array_2d *a); + +/** + * array_2d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_2d_print(const array_2d * l, inspect_callback print_func); + +/** + * array_2d_print_internal() - Print the arrays internal structure. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/dlist.h b/OU1/datastructures-v2.2.2.2/include/dlist.h new file mode 100644 index 0000000..8900bc8 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/dlist.h @@ -0,0 +1,199 @@ +#ifndef __DLIST_H +#define __DLIST_H + +#include +#include "util.h" + +/* + * Declaration of a generic, directed list for the "Datastructures + * and algorithms" courses at the Department of Computing Science, + * Umea University. The list stores void pointers, so it can be used + * to store all types of values. After use, the function list_kill + * must be called to de-allocate the dynamic memory used by the list + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the list, + * unless a kill_function is registered in list_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added dlist_pos_equal and dlist_pos_is_valid functions. + * v1.2 2023-01-20: Renamed dlist_pos_equal to dlist_pos_are_equal. + * v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct dlist dlist; + +// List position type. +typedef struct cell *dlist_pos; + + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * dlist_empty() - Create an empty dlist. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +dlist *dlist_empty(kill_function kill_func); + +/** + * dlist_is_empty() - Check if a dlist is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool dlist_is_empty(const dlist *l); + +/** + * dlist_first() - Return the first position of a dlist, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +dlist_pos dlist_first(const dlist *l); + +/** + * dlist_next() - Return the next position in a dlist. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +dlist_pos dlist_next(const dlist *l, const dlist_pos p); + +/** + * dlist_is_end() - Check if a given position is at the end of a dlist. + * @l: List to inspect. + * @p: Any valid position in the list. + * + * Returns: True if p is at the end of the list. + */ +bool dlist_is_end(const dlist *l, const dlist_pos p); + +/** + * dlist_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *dlist_inspect(const dlist *l, const dlist_pos p); + +/** + * dlist_insert() - Insert a new element with a given value into a dlist. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p); + +/** + * dlist_remove() - Remove an element from a dlist. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +dlist_pos dlist_remove(dlist *l, const dlist_pos p); + +/** + * dlist_kill() - Destroy a given dlist. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void dlist_kill(dlist *l); + +/** + * dlist_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void dlist_print(const dlist *l, inspect_callback print_func); + +/** + * dlist_pos_is_valid() - Return true for a valid position in a dlist + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool dlist_pos_is_valid(const dlist *l, const dlist_pos p); + +/** + * dlist_pos_is_equal() - Return true if two positions in a dlist are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2); + +/** + * dlist_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/int_array_1d.h b/OU1/datastructures-v2.2.2.2/include/int_array_1d.h new file mode 100644 index 0000000..9b53387 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/int_array_1d.h @@ -0,0 +1,136 @@ +#ifndef __INT_ARRAY_1D_H +#define __INT_ARRAY_1D_H + +#include +#include "util.h" + +/* + * Declaration of an integer 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. After use, the function array_kill must be called to + * de-allocate the dynamic memory used by the array itself. + * + * An element value of 0 is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.2 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct int_array_1d int_array_1d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * int_array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +int_array_1d *int_array_1d_create(int lo, int hi); + +/** + * int_array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int int_array_1d_low(const int_array_1d *a); + +/** + * int_array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int int_array_1d_high(const int_array_1d *a); + +/** + * int_array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position, or 0 if no + * value is stored at that position. + */ +int int_array_1d_inspect_value(const int_array_1d *a, int i); + +/** + * int_array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool int_array_1d_has_value(const int_array_1d *a, int i); + +/** + * int_array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or 0 to clear value. + * @i: index of position to modify. + * + * Returns: Nothing. + */ +void int_array_1d_set_value(int_array_1d *a, int v, int i); + +/** + * int_array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If free_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void int_array_1d_kill(int_array_1d *a); + +/** + * int_array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * + * Iterates over each position in the array. Prints each non-zero element. + * + * Returns: Nothing. + */ +void int_array_1d_print(const int_array_1d *a); + +/** + * int_array_1d_print_internal() - Print the arrays internal structure in dot format. + * @a: Array to inspect. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/int_list.h b/OU1/datastructures-v2.2.2.2/include/int_list.h new file mode 100644 index 0000000..30e9549 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/int_list.h @@ -0,0 +1,192 @@ +#ifndef __INT_LIST_H +#define __INT_LIST_H + +#include +#include "util.h" + +/* + * Declaration of a undirected list for storing integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * dynamic, linked structure. After use, the function list_kill must + * be called to de-allocate the dynamic memory used by the list + * itself. The implementation is a written a code copy specialization + * of the generic list to provide a simpler starting data structure + * than the generic list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef struct cell *list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @data: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, int data, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * + * Iterates over the list and prints each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list *l); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/int_list_array.h b/OU1/datastructures-v2.2.2.2/include/int_list_array.h new file mode 100644 index 0000000..9db2f74 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/int_list_array.h @@ -0,0 +1,193 @@ +#ifndef __INT_LIST_ARRAY_H +#define __INT_LIST_ARRAY_H + +#include +#include "util.h" + +/* + * Declaration of a undirected list for storing integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * static array. After use, the function list_kill must be called to + * de-allocate the dynamic memory used by the list itself. The + * implementation is a written a code copy specialization of the + * generic list to provide a simpler starting data structure than the + * generic list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-03-26: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * Bugfix in list_remove. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef int list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @data: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, int data, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * + * Iterates over the list and prints each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list *l); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/int_queue.h b/OU1/datastructures-v2.2.2.2/include/int_queue.h new file mode 100644 index 0000000..45a8c2e --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/int_queue.h @@ -0,0 +1,126 @@ +#ifndef __INT_QUEUE_H +#define __INT_QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of an integer queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores integers directly and does not use + * dynamic memory. Thus, the clean-up function queue_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic queue implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +// ==========PUBLIC CONSTANTS AND DATA TYPES============ + +// Maximum size of the queue. +#define MAX_QUEUE_SIZE 100 + +// We must define the struct publically for the compiler to know its +// size for copying, etc. +typedef struct queue { + int first_free_pos; + int elements[MAX_QUEUE_SIZE]; +} queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * + * Returns: A new, empty, queue. + */ +queue queue_empty(void); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue q); + +/** + * queue_enqueue() - Put a value at the end of a queue. + * @q: Queue to manipulate. + * @v: Value (integer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue queue_enqueue(queue q, int v); + +/** + * queue_dequeue() - Remove the element at the beginning of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue queue_dequeue(queue q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the front of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +int queue_front(const queue q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Does nothing since the queue does not use any dynamic + * memory. Included for symmetry with generic queue.h. + * + * Returns: Nothing. + */ +void queue_kill(queue q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * + * Iterates over the queue and prints each integer. + * + * Returns: Nothing. + */ +void queue_print(const queue q); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the queue and outputs dot code that shows the + * internal structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/int_stack.h b/OU1/datastructures-v2.2.2.2/include/int_stack.h new file mode 100644 index 0000000..a64cb28 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/int_stack.h @@ -0,0 +1,134 @@ +#ifndef __INT_STACK_H +#define __INT_STACK_H + +#include +#include "util.h" + +/* + * Declaration of an integer stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores integers directly and does not use + * dynamic memory. Thus, the clean-up function stack_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic stack implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-04-15: Reduced default stack size to 15 to make output + * from print_internal readable. + * v1.2 2024-05-10: Updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Stack type. +#define MAX_STACK_SIZE 100 + +// We must define the struct publically for the compiler to know its +// size for copying, etc. +typedef struct stack { + int first_free_pos; + int elements[MAX_STACK_SIZE]; +} stack; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * stack_empty() - Create an empty stack. + * + * Returns: A new stack. + */ +stack stack_empty(void); + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack s); + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (integer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_push(stack s, int v); + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_pop(stack s); + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +int stack_top(const stack s); + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Does nothing since the stack does not use any dynamic + * memory. Included for symmetry with generic stack.h. + * + * Returns: Nothing. + */ +void stack_kill(stack s); + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * + * Iterates over the stack and prints each integer. + * + * Returns: Nothing. + */ +void stack_print(const stack s); + +/** + * stack_print_internal() - Print the internal structure of the stack in dot format. + * @s: Stack to inspect. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the stack and outputs dot code that shows the + * internal structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/list.h b/OU1/datastructures-v2.2.2.2/include/list.h new file mode 100644 index 0000000..a65d7bd --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/list.h @@ -0,0 +1,207 @@ +#ifndef __LIST_H +#define __LIST_H + +#include +#include "util.h" + +/* + * Declaration of a generic, undirected list for the "Datastructures + * and algorithms" courses at the Department of Computing Science, + * Umea University. The list stores void pointers, so it can be used + * to store all types of values. After use, the function list_kill + * must be called to de-allocate the dynamic memory used by the list + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the list, + * unless a kill_function is registered in list_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v2.0 2024-03-14: Added list_print_internal to output dot code for visualization. + * v2.1 2024-05-10: Updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef struct cell *list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +list *list_empty(kill_function kill_func); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, void *v, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void list_print(const list *l, inspect_callback print_func); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/queue.h b/OU1/datastructures-v2.2.2.2/include/queue.h new file mode 100644 index 0000000..1070e98 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/queue.h @@ -0,0 +1,131 @@ +#ifndef __QUEUE_H +#define __QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores void pointers, so it can be used to + * store all types of values. After use, the function queue_kill must + * be called to de-allocate the dynamic memory used by the queue + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the queue, + * unless a kill_function is registered in queue_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Queue type. +typedef struct queue queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q); + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v); + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/stack.h b/OU1/datastructures-v2.2.2.2/include/stack.h new file mode 100644 index 0000000..2dfef95 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/stack.h @@ -0,0 +1,133 @@ +#ifndef __STACK_H +#define __STACK_H + +#include +#include "util.h" + +/* + * Declaration of a generic stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores void pointers, so it can be used to + * store all types of values. After use, the function stack_kill must + * be called to de-allocate the dynamic memory used by the stack + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the stack, + * unless a kill_function is registered in stack_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Stack type. +typedef struct stack stack; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * stack_empty() - Create an empty stack. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new stack. + */ +stack *stack_empty(kill_function kill_func); + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack *s); + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (pointer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_push(stack *s, void *v); + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_pop(stack *s); + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +void *stack_top(const stack *s); + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Return all dynamic memory used by the stack and its elements. If a + * kill_func was registered at stack creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void stack_kill(stack *s); + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * @print_func: Function called for each element. + * + * Iterates over the stack and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void stack_print(const stack *s, inspect_callback print_func); + +/** + * stack_print_internal() - Print the stacks internal structure in dot format. + * @l: Stack to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the stack and outputs dot code that shows the internal + * structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/table.h b/OU1/datastructures-v2.2.2.2/include/table.h new file mode 100644 index 0000000..1a7d309 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/table.h @@ -0,0 +1,157 @@ +#ifndef TABLE_H +#define TABLE_H + +#include +#include "util.h" + +/* + * Declaration of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The table stores void pointers, so it can be used to + * store all types of values. After use, the function table_kill must + * be called to de-allocate the dynamic memory used by the table + * itself. The de-allocation of any dynamic memory allocated for the + * key and/or value values is the responsibility of the user of the + * table, unless a corresponding kill_function is registered in + * table_empty. + * + * The internal handling of duplicates are transparent to the user of + * the table. Depending on the table design, a duplicate may or may + * not be stored internally. The user of the table should not make any + * assumption about which solution is implemented. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2024-04-15: Changed the comments to leave room for different + * internal handling of duplicates. + * v1.2 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ +// Table type. +typedef struct table table; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. See + * util.h for the definition of compare_function. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func); + +/** + * table_is_empty() - Check if a table is empty. + * @t: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t); + +/** + * table_insert() - Add a key/value pair to a table. + * @t: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. If the key already + * exists, the key/value overwrites the existing pair. The technical + * details are internal to the datatype. At any rate, table_lookup() + * will return the latest added value for a duplicate + * key and table_remove() will remove all duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value); + +/** + * table_lookup() - Look up a given key in a table. + * @t: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key); + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t); + +/** + * table_remove() - Remove a key/value pair in the table. + * @t: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key); + +/** + * table_kill() - Destroy a table. + * @t: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to free any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t); + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including any duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func); + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU1/datastructures-v2.2.2.2/include/util.h b/OU1/datastructures-v2.2.2.2/include/util.h new file mode 100644 index 0000000..db3681e --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/include/util.h @@ -0,0 +1,83 @@ +#ifndef __UTIL_H +#define __UTIL_H + +/* + * Utility function types for deallocating, printing and comparing + * values stored by various data types. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-02-06: Updated explanation for the compare_function. + * v1.2 2023-01-14: Added version define constants and strings. + * v1.3 2024-03-13: Added PTR2ADDR macro. + */ + +// Macros to create a version string out of version constants +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +// Version constants +#define CODE_BASE_MAJOR_VERSION 2 +#define CODE_BASE_MINOR_VERSION 2 +#define CODE_BASE_REVISION 2 +#define CODE_BASE_PATCH 2 + +#define CODE_BASE_RELEASE_DATE "2025-01-24" + +// Create a short version string +#define CODE_BASE_VERSION "v" \ + STR(CODE_BASE_MAJOR_VERSION) \ + "." \ + STR(CODE_BASE_MINOR_VERSION) \ + "." \ + STR(CODE_BASE_REVISION) \ + "." \ + STR(CODE_BASE_PATCH) + +// Create a version string +#define CODE_BASE_LONG_VERSION "Version: " \ + STR(CODE_BASE_MAJOR_VERSION) \ + "." \ + STR(CODE_BASE_MINOR_VERSION) \ + "." \ + STR(CODE_BASE_REVISION) \ + "." \ + STR(CODE_BASE_PATCH) + +// Type definition for de-allocator function, e.g. free(). +typedef void (*kill_function)(void *); + +// For backwards compatibility with pre-v2.0 code. +typedef kill_function free_function; + +// Type definition for read-only callback for single-value containers, +// used by e.g. print functions. +typedef void (*inspect_callback)(const void *); + +// Ditto for dual-value containers. +typedef void (*inspect_callback_pair)(const void *, const void *); + +// Type definition for comparison function, used by e.g. table. +// +// Comparison functions should return values that indicate the order +// of the arguments. If the first argument is considered less/lower +// than the second, a negative value should be returned. If the first +// argument is considered more/higher than the second, a positive value +// should be returned. If the arguments are considered equal, a zero +// value should be returned. +typedef int compare_function(const void *,const void *); + +// Constant used by ptr2addr, used by various print_internal functions. +#define PTR2ADDR_MASK 0xffff + +// Macro that acts as a function to convert the address in the pointer +// p into an unsigned long value, keeping only the part indicated by +// the mask. +#define PTR2ADDR(p) (((unsigned long)p) & PTR2ADDR_MASK) + +#endif diff --git a/OU1/datastructures-v2.2.2.2/lib/.gitignore b/OU1/datastructures-v2.2.2.2/lib/.gitignore new file mode 100644 index 0000000..9ae31f6 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/lib/.gitignore @@ -0,0 +1 @@ +libdoa.a diff --git a/OU1/datastructures-v2.2.2.2/lib/Makefile b/OU1/datastructures-v2.2.2.2/lib/Makefile new file mode 100644 index 0000000..b50c5c1 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/lib/Makefile @@ -0,0 +1,34 @@ +SRC = ../src/list/list.c ../src/stack/stack.c \ + ../src/array_2d/array_2d.c ../src/table/table.c \ + ../src/table/table2.c ../src/array_1d/array_1d.c \ + ../src/queue/queue.c ../src/dlist/dlist.c \ + ../src/version/version.c +H = ../include/queue.h ../include/dlist.h ../include/array_2d.h \ + ../include/util.h ../include/table.h ../include/list.h \ + ../include/array_1d.h ../include/stack.h + +OBJ = $(SRC:.c=.o) + +LIB = libdoa.a + +CC = gcc +CFLAGS = -std=c99 -Wall -I../include -g + +all: lib + +# Library +lib: $(LIB) + +# Object file for library +$(LIB): $(OBJ) $(H) + $(AR) r $@ $(OBJ) + ranlib $@ + +# Clean up +clean: + -rm -f $(OBJ) + +# Clean up +cleaner: + -rm -f $(LIB) $(OBJ) + diff --git a/OU1/datastructures-v2.2.2.2/src/Makefile b/OU1/datastructures-v2.2.2.2/src/Makefile new file mode 100644 index 0000000..e3d0733 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/Makefile @@ -0,0 +1,13 @@ +# List of directories containing sub-projects +SUB_DIRS := array_1d array_2d dlist int_array_1d int_list int_list_array int_stack list int_queue queue stack table + +# Rule to call 'make all' in each sub-directory +all: + for dir in $(SUB_DIRS); do \ + $(MAKE) -C $$dir all; \ + done + +clean: + for dir in $(SUB_DIRS); do \ + $(MAKE) -C $$dir clean; \ + done diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/Makefile b/OU1/datastructures-v2.2.2.2/src/array_1d/Makefile new file mode 100644 index 0000000..ad8fc86 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/Makefile @@ -0,0 +1,49 @@ +MWE = array_1d_mwe1 array_1d_mwe2 array_1d_mwe3 array_1d_mwe4 array_1d_mwe1i array_1d_mwe2i + +SRC = array_1d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +array_1d_mwe1: array_1d_mwe1.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe2: array_1d_mwe2.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe3: array_1d_mwe3.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe4: array_1d_mwe4.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe1i: array_1d_mwe1i.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe2i: array_1d_mwe2i.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: array_1d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: array_1d_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: array_1d_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: array_1d_mwe4 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d.c new file mode 100644 index 0000000..6244fac --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d.c @@ -0,0 +1,491 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-05-10: Added print_internal. + */ + +// =========== INTERNAL DATA TYPES ============ + +struct array_1d { + int low; // Low index limit. + int high; // High index limit. + int array_size; // Number of array elements. + void **values; // Pointer to where the actual values are stored. + kill_function kill_func; +}; + +// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_1d *array_1d_create(int lo, int hi, kill_function kill_func) +{ + // Allocate array structure. + array_1d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low = lo; + a->high = hi; + + // Number of elements. + a->array_size = hi - lo + 1; + + // Store kill function. + a->kill_func = kill_func; + + a->values = calloc(a->array_size, sizeof(void *)); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int array_1d_low(const array_1d *a) +{ + return a->low; +} + +/** + * array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int array_1d_high(const array_1d *a) +{ + return a->high; +} + +/** + * array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_1d_inspect_value(const array_1d *a, int i) +{ + int offset = i - array_1d_low(a); + // Return the value. + return a->values[offset]; +} + +/** + * array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_1d_has_value(const array_1d *a, int i) +{ + int offset = i - array_1d_low(a); + // Return true if the value is not NULL. + return a->values[offset] != NULL; +} + +/** + * array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_1d_set_value(array_1d *a, void *v, int i) +{ + int offset = i - array_1d_low(a); + // Call kill_func if specified and old element value was non-NULL. + if (a->kill_func != NULL && a->values[offset] != NULL) { + a->kill_func( a->values[offset] ); + } + // Set value. + a->values[offset] = v; +} + +/** + * array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_1d_kill(array_1d *a) +{ + if (a->kill_func != NULL) { + // Return user-allocated memory for each non-NULL element. + for (int i = 0; i < a->array_size; i++) { + if (a->values[i] != NULL) { + a->kill_func(a->values[i]); + } + } + } + // Return memory for value pointers. + free(a->values); + // Return memory for array structure. + free(a); +} + +/** + * array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_1d_print(const array_1d *a, inspect_callback print_func) +{ + printf("[ "); + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + printf("["); + print_func(array_1d_inspect_value(a, i)); + printf("]"); + } else { + printf(" []"); + } + if (i < array_1d_high(a)) { + printf(", "); + } + } + printf(" ]\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY array_1d_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|low\\n%d|high\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(a), PTR2ADDR(a->kill_func), a->low, a->high, a->array_size, + PTR2ADDR(a->values)); +} + +void print_values(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int i=0; i < a->array_size; i++) { + printf("<%02d>%d\\n%02d\\n%04lx", i, i + a->low, i, PTR2ADDR(a->values[i])); + if (i < a->array_size - 1) { + printf("|"); + } + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a)); + if (a->values == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(a->values)); + } + printf(" [label=\"values\"]\n"); +} + +// Print nodes for each value memory block +static void print_value_nodes(int indent_level, const array_1d *a, inspect_callback print_func) +{ + for (int i=0; i <= a->array_size; i++) { + if (a->values[i] != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i])); + if (print_func != NULL) { + print_func(a->values[i]); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i])); + } + } +} + +// Print edges from each value pointer to payload memory +static void print_value_edges(int indent_level, const array_1d *a) +{ + for (int i=0; i < a->array_size; i++) { + // Buffer to store port name in. Good for array up to 1e9 elements. + char port[15]; + // Create port name + sprintf(port, "%02d", i); + if (a->kill_func) { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red"); + } else { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red style=dashed"); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * array_1d_print_internal() - Print the internal structure of the array in dot format. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_1D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_array_1d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array + print_values(il, a); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Print nodes for each value memory block + print_value_nodes(il, a, print_func); + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, a); + + // Print edges from each value pointer to payload memory + print_value_edges(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe1.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe1.c new file mode 100644 index 0000000..5664909 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe1.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * Removed use of automatic, magic, free_handler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + * v1.31 2024-04-05: Bugfix: Remove call to array_1d_print_internal + * that shouldn't be here. + */ + +#define VERSION "v1.31" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, NULL); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a, v, i); + } + printf("Array after inserting i^2, i=1,...,6:\n"); + array_1d_print(a, print_int); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + int *v = array_1d_inspect_value(a, i); + int_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe1i.c new file mode 100644 index 0000000..fba94d1 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe1i.c @@ -0,0 +1,146 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-05: Adapted from array_1d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_1d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, NULL); + + printf("Empty array from the outside:\n"); + array_1d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + printf("Array from the outside after setting half the values:\n"); + array_1d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting 3 values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_1d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_1d_high(a) ; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + + printf("Array from the outside after setting all the values:\n"); + array_1d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all 6 values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_1d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + int *v=array_1d_inspect_value(a,i); + int_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe2.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe2.c new file mode 100644 index 0000000..04b002f --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe2.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 2 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, int_kill); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a, v, i); + } + printf("Array after inserting i^2, i=1,...,6:\n"); + array_1d_print(a, print_int); + + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe2i.c new file mode 100644 index 0000000..9462bb8 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe2i.c @@ -0,0 +1,140 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-05: Adapted from array_1d_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_1d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, int_kill); + + printf("Empty array from the outside:\n"); + array_1d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + printf("Array from the outside after setting half the values:\n"); + array_1d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting 3 values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_1d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_1d_high(a) ; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + + printf("Array from the outside after setting all the values:\n"); + array_1d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all 6 values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_1d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Clean up the array. + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe3.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe3.c new file mode 100644 index 0000000..318de8f --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe3.c @@ -0,0 +1,99 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 3 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * Removed use of automatic, magic, free_handler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Struct with month name and number of days. +typedef struct month { + char *name; + int days; +} month; + +// Months are stored via void pointers. Convert the given pointer and +// print the dereferenced values. +void print_month(const void *data) +{ + // Convert void pointer to pointer to month. + const month *m=data; + printf("(%s, %d)", m->name, m->days); +} + +// Allocate space for and populate a month structure +month *month_create(const char *name, int days) +{ + // Allocate memory for a month structure. + month *m = malloc(sizeof(*m)); + // Allocate memory for the month name. + m->name = calloc(strlen(name) + 1, sizeof(char)); + // Copy the string. + strcpy(m->name, name); + // Set days. + m->days = days; + return m; +} + +// Function to free both month structure and char * with name. +void month_kill(void *data) +{ + // Convert void pointer to pointer to month. + month *m=data; + free(m->name); + free(m); +} + +int main(void) +{ + printf("%s, %s %s: Create month array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + const char *month_names[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int month_days[12] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + // Create an array with 12 positions. + array_1d *a = array_1d_create(1, 12, NULL); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate and populate a month structure. + month *m=month_create(month_names[i - 1], month_days[i - 1]); + // Set value in array. + array_1d_set_value(a,m,i); + } + printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n"); + array_1d_print(a, print_month); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a,i)) { + month *v=array_1d_inspect_value(a,i); + month_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe4.c b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe4.c new file mode 100644 index 0000000..24ed388 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_1d/array_1d_mwe4.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 4 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Struct with month name and number of days. +typedef struct month { + char *name; + int days; +} month; + +// Months are stored via void pointers. Convert the given pointer and +// print the dereferenced values. +void print_month(const void *data) +{ + // Convert void pointer to pointer to month. + const month *m=data; + printf("(%s, %d)", m->name, m->days); +} + +// Allocate space for and populate a month structure +month *month_create(const char *name, int days) +{ + // Allocate memory for a month structure. + month *m = malloc(sizeof(*m)); + // Allocate memory for the month name. + m->name = calloc(strlen(name) + 1, sizeof(char)); + // Copy the string. + strcpy(m->name, name); + // Set days. + m->days = days; + return m; +} + +// Function to free both month structure and char * with name. +void month_kill(void *data) +{ + // Convert void pointer to pointer to month. + month *m=data; + free(m->name); + free(m); +} + +int main(void) +{ + printf("%s, %s %s: Create month array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + const char *month_names[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int month_days[12] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + // Create an array with 12 positions. + array_1d *a = array_1d_create(1, 12, month_kill); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate and populate a month structure. + month *m=month_create(month_names[i - 1], month_days[i - 1]); + // Set value in array. + array_1d_set_value(a,m,i); + } + printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n"); + array_1d_print(a, print_month); + + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_2d/Makefile b/OU1/datastructures-v2.2.2.2/src/array_2d/Makefile new file mode 100644 index 0000000..e64cc6a --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_2d/Makefile @@ -0,0 +1,37 @@ +MWE = array_2d_mwe1 array_2d_mwe2 array_2d_mwe1i array_2d_mwe2i + +SRC = array_2d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +array_2d_mwe1: array_2d_mwe1.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe2: array_2d_mwe2.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe1i: array_2d_mwe1i.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe2i: array_2d_mwe2i.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: array_2d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: array_2d_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d.c b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d.c new file mode 100644 index 0000000..2c10938 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d.c @@ -0,0 +1,526 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 2D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Moved freehandler to last in create parameter list. + * v1.2 2024-05-10: Added print_internal. + * v1.21 2024-05-15: Bugfix to get rid of sprintf warning. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct array_2d { + int low[2]; // Low index limits. + int high[2]; // High index limits. + int array_size; // Number of array elements. + void **values; // Pointer to where the actual values are stored. + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * array_2d_create() - Create an array without values. + * @lo1: low index limit for first dimension. + * @hi1: high index limit for first dimension. + * @lo2: low index limit for second dimension. + * @hi2: high index limit for second dimension. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2, + kill_function kill_func) +{ + // Allocate array structure. + array_2d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low[0] = lo1; + a->low[1] = lo2; + a->high[0] = hi1; + a->high[1] = hi2; + + // Number of elements. + a->array_size = (hi1 - lo1 + 1) * (hi2 - lo2 + 1); + + // Store kill function. + a->kill_func = kill_func; + + a->values=calloc(a->array_size, sizeof(void *)); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * array_2d_low() - Return the low index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The low index limit for dimension number d. + */ +int array_2d_low(const array_2d *a, int d) +{ + return a->low[d - 1]; +} + +/** + * array_2d_high() - Return the high index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The high index limit for dimension number d. + */ +int array_2d_high(const array_2d *a, int d) +{ + return a->high[d - 1]; +} + +/** + * array_2d_linear_index() - Internal function to compute linear index from list + * of indices. + * @a: array to inspect. + * @i: First index. + * @j: Second index. + * + * Returns: The linear index corresponding to the list of indices. + * NOTE: The result is undefined if the number of arguments + * or index value are out of bounds. + */ +static int array_2d_linear_index(const array_2d *a, int i, int j) +{ + int rows = a->high[0] - a->low[0] + 1; + int ix=(i - a->low[0]) + (j - a->low[1]) * rows; + return ix; +} + +/** + * array_2d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: First index of position to inspect. + * @j: Second index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_2d_inspect_value(const array_2d *a, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Return the value. + return a->values[ix]; +} + +/** + * array_2d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: First index of position to inspect. + * @j: Second index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_2d_has_value(const array_2d *a, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Return true if the value is not NULL. + return a->values[ix] != NULL; +} + +/** + * array_2d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: First index of position to modify. + * @j: Second index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_2d_set_value(array_2d *a, void *v, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Call kill_func if specified and old element value was non-NULL. + if (a->kill_func != NULL && a->values[ix] != NULL) { + a->kill_func(a->values[ix]); + } + // Set value. + a->values[ix]=v; +} + +/** + * array_2d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_2d_kill(array_2d *a) +{ + if (a->kill_func) { + // Return user-allocated memory for each non-NULL element. + for (int i=0; iarray_size; i++) { + if (a->values[i] != NULL) { + a->kill_func(a->values[i]); + } + } + } + // Free actual storage. + free(a->values); + // Free array structure. + free(a); +} + +/** + * array_2d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_2d_print(const array_2d *a, inspect_callback print_func) +{ + printf("[\n"); + for (int i=array_2d_low(a,1); i<=array_2d_high(a,1); i++) { + printf(" [ "); + for (int j=array_2d_low(a,2); j<=array_2d_high(a,2); j++) { + if (array_2d_has_value(a,i,j)) { + printf("["); + print_func(array_2d_inspect_value(a,i,j)); + printf("]"); + } else { + printf("[ ]"); + } + if (j ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const array_2d *p) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"func\\n%04lx|low[0]\\n%d|high[0]\\n%d" + "|low[1]\\n%d|high[1]\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->kill_func), p->low[0], p->high[0], + p->low[1], p->high[1], p->array_size, PTR2ADDR(p->values)); +} + +static void print_values(int indent_level, const array_2d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int j=array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + for (int i=array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + int li=array_2d_linear_index(a, i, j); + printf("<%02d>(%d,%d)\\n%02d\\n%04lx", + li, i, j, li, PTR2ADDR(a->values[li])); + if (li < a->array_size - 1) { + printf("|"); + } + } + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const array_2d *a) +{ + iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a)); + if (a->values == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(a->values)); + } + printf(" [label=\"values\"]\n"); +} + +// Print nodes for each value memory block +static void print_value_nodes(int indent_level, const array_2d *a, inspect_callback print_func) +{ + for (int i=0; i <= a->array_size; i++) { + if (a->values[i] != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i])); + if (print_func != NULL) { + print_func(a->values[i]); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i])); + } + } +} + +// Print edges from each value pointer to payload memory +static void print_value_edges(int indent_level, const array_2d *a) +{ + for (int i=0; i < a->array_size; i++) { + // Buffer to store port name in. Good for array up to 1e9 elements. + char port[15]; + // Create port name + sprintf(port, "%02d", i); + if (a->kill_func) { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red"); + } else { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red style=dashed"); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * array_2d_print_internal() - Print the arrays internal structure. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_2D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_array_2d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array + print_values(il, a); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Print nodes for each value memory block + print_value_nodes(il, a, print_func); + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, a); + + // Print edges from each value pointer to payload memory + print_value_edges(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe1.c b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe1.c new file mode 100644 index 0000000..4261433 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe1.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2023-01-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, NULL); + + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("After filling the array with values:\n"); + array_2d_print(a, print_int); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe1i.c new file mode 100644 index 0000000..6a600e7 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe1i.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-07: Adapted from array_2d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-07" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_2d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_2d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, NULL); + + printf("Empty array from the outside:\n"); + array_2d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("Array from the outside after setting half the values:\n"); + array_2d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting half of the values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_2d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + + printf("Array from the outside after setting all the values:\n"); + array_2d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all the values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_2d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe2.c b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe2.c new file mode 100644 index 0000000..bc48a0f --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe2.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2023-01-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, int_kill); + + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("After filling the array with values:\n"); + array_2d_print(a, print_int); + + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe2i.c new file mode 100644 index 0000000..ecb1167 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/array_2d/array_2d_mwe2i.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-07: Adapted from array_2d_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-07" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_2d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_2d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, int_kill); + + printf("Empty array from the outside:\n"); + array_2d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("Array from the outside after setting half the values:\n"); + array_2d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting half of the values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_2d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + + printf("Array from the outside after setting all the values:\n"); + array_2d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all the values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_2d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/Makefile b/OU1/datastructures-v2.2.2.2/src/dlist/Makefile new file mode 100644 index 0000000..914da3e --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/Makefile @@ -0,0 +1,42 @@ +MWE = dlist_mwe1 dlist_mwe2 dlist_mwe1i dlist_mwe2i + +SRC = dlist.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +clean: + -rm -f $(MWE) $(OBJ) + +dlist_mwe1: dlist_mwe1.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe2: dlist_mwe2.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe1i: dlist_mwe1i.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe2i: dlist_mwe2i.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: dlist_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: dlist_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: dlist_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: dlist_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/README.md b/OU1/datastructures-v2.2.2.2/src/dlist/README.md new file mode 100644 index 0000000..85f7b69 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/README.md @@ -0,0 +1,9 @@ +# Riktad lista + +En implementation av ADTn _Riktad Lista_ baserad på en enkel-länkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/dlist.c b/OU1/datastructures-v2.2.2.2/src/dlist/dlist.c new file mode 100644 index 0000000..414c8dc --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/dlist.c @@ -0,0 +1,588 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic, undirected list for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added dlist_pos_are_equal and dlist_pos_is_valid functions. + * v1.2 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list elements are implemented as one-cells with a forward link. + * The list position is a pointer to the internal cell before the cell + * with the value. + */ +typedef struct cell { + struct cell *next; + void *val; +} cell; + + +struct dlist { + cell *head; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * dlist_empty() - Create an empty dlist. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +dlist *dlist_empty(kill_function kill_func) +{ + // Allocate memory for the list structure. + dlist *l = calloc(1, sizeof(*l)); + + // Allocate memory for the list head. + l->head = calloc(1, sizeof(cell)); + + // No elements in list so far. + l->head->next = NULL; + + // Store the kill function. + l->kill_func = kill_func; + + return l; +} + +/** + * dlist_is_empty() - Check if a dlist is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool dlist_is_empty(const dlist *l) +{ + return (l->head->next == NULL); +} + +/** + * dlist_first() - Return the first position of a dlist, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +dlist_pos dlist_first(const dlist *l) +{ + return l->head; +} + +/** + * dlist_next() - Return the next position in a dlist. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +dlist_pos dlist_next(const dlist *l, const dlist_pos p) +{ + if (dlist_is_end(l, p)) { + // This should really throw an error. + fprintf(stderr,"dlist_next: Warning: Trying to navigate " + "past end of list!"); + } + return p->next; +} + +/** + * dlist_is_end() - Check if a given position is at the end of a dlist. + * @l: List to inspect. + * @p: Any valid position in the list. + * + * Returns: True if p is at the end of the list. + */ +bool dlist_is_end(const dlist *l, const dlist_pos p) +{ + return p->next == NULL; +} + +/** + * dlist_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *dlist_inspect(const dlist *l, const dlist_pos p) +{ + if (dlist_is_end(l, p)) { + // This should really throw an error. + fprintf(stderr,"dlist_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->next->val; +} + +/** + * dlist_insert() - Insert a new element with a given value into a dlist. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p) +{ + // Create new element. + dlist_pos new_pos=calloc(1, sizeof(cell)); + // Set value. + new_pos->val=v; + + // Set links. + new_pos->next=p->next; + p->next=new_pos; + + return p; +} + +/** + * dlist_remove() - Remove an element from a dlist. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +dlist_pos dlist_remove(dlist *l, const dlist_pos p) +{ + // Cell to remove. + dlist_pos c=p->next; + + // Link past cell to remove. + p->next=c->next; + + // Call kill_func if registered. + if(l->kill_func != NULL) { + // Return any user-allocated memory for the value. + l->kill_func(c->val); + } + // Free the memory allocated to the cell itself. + free(c); + // Return the position of the next element. + return p; +} + +/** + * dlist_kill() - Destroy a given dlist. + * @l: List to destroy. + * + * Return all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void dlist_kill(dlist *l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + dlist_pos p = dlist_first(l); + + // Remove first element until list is empty. + while(!dlist_is_empty(l)) { + p = dlist_remove(l, p); + } + + // Free the head and the list itself. + free(l->head); + free(l); +} + +/** + * dlist_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void dlist_print(const dlist *l, inspect_callback print_func) +{ + // Start at the beginning of the list. + dlist_pos p = dlist_first(l); + + printf("( "); + while (!dlist_is_end(l, p)) { + // Call print_func with the element value at the + // current position. + print_func(dlist_inspect(l, p)); + // Advance to next position. + p = dlist_next(l, p); + // Print separator unless at element. + if (!dlist_is_end(l, p)) { + printf(", "); + } + } + printf(" )\n"); +} + +/** + * dlist_pos_is_equal() - Return true if two positions in a dlist are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * dlist_pos_is_valid() - Return true for a valid position in a dlist. + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool dlist_pos_is_valid(const dlist *l, const dlist_pos p) +{ + // Iterate over all positions in l. + dlist_pos q = dlist_first(l); + while (!dlist_is_end(l, q)) { + if (dlist_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = dlist_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the dlist head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const dlist *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|head\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l)); +} + +// Print edges from the list head to the head cell. +static void print_head_edges(int indent_level, const dlist *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const dlist_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%04lx|next\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const dlist *l, const dlist_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + + // Ignore val ptr for head and tail nodes. + if (p == l->head) { + return; + } + if (l->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * dlist_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph DLIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_dlist_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_dlist_%d { label=\"DList\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + dlist_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = l->head; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe1.c b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe1.c new file mode 100644 index 0000000..2f2d14b --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe1.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for dlist.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit copy/kill string functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// Test program. +int main(void) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(NULL); + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList after inserting 6 strings:\n"); + dlist_print(l, print_string); + + // Traverse the list and deallocate each value. + p = dlist_first(l); + while (!dlist_is_end(l,p)) { + char *v=dlist_inspect(l,p); + kill_string(v); + p=dlist_remove(l,p); + } + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe1i.c new file mode 100644 index 0000000..fd9ff6d --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe1i.c @@ -0,0 +1,142 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for dlist.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "borrows" the payload memory, i.e., the user of the list is + * responsible for deallocating the payload memory. See dlist_mwe2i.c + * for a version where the list takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +void print_string_internal(const void *value) +{ + const char *s=value; + // We must double-escape the double quotes since the output will + // be parsed a second time by graphviz. + printf("\\\"%s\\\"", s); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const dlist *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + dlist_print_internal(l, print_string_internal, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +// Test program. +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(NULL); + + printf("Empty list from the outside:\n"); + dlist_print(l, print_string); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList from the outside after inserting 6 strings:\n"); + dlist_print(l, print_string); + const char *long_desc = __FILE__ + ": Internal structure of the DList after inserting 6 strings.\n" + "Red lines are used for the list payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the list, i.e., the payload\n" + "memory will NOT be deallocated by the list.\n\n" + "See dlist_mwe2i for a list example\nthat owns the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Traverse the list and deallocate each value. + p = dlist_first(l); + while (!dlist_is_end(l,p)) { + char *v=dlist_inspect(l,p); + kill_string(v); + p=dlist_remove(l,p); + } + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe2.c b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe2.c new file mode 100644 index 0000000..0db799a --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe2.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 2 for dlist.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit copy/kill string functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// Test program. +int main(void) +{ + printf("%s, %s %s: Create list of strings with standard memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(kill_string); + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList after inserting 6 strings:\n"); + dlist_print(l, print_string); + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe2i.c new file mode 100644 index 0000000..ceef4fc --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/dlist/dlist_mwe2i.c @@ -0,0 +1,136 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for dlist.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "owns" the payload memory, i.e., the list takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See list_mwe1i.c for a version + * where the list does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +void print_string_internal(const void *value) +{ + const char *s=value; + // We must double-escape the double quotes since the output will + // be parsed a second time by graphviz. + printf("\\\"%s\\\"", s); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const dlist *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + dlist_print_internal(l, print_string_internal, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +// Test program. +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(kill_string); + + printf("Empty list from the outside:\n"); + dlist_print(l, print_string); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList from the outside after inserting 6 strings:\n"); + dlist_print(l, print_string); + const char *long_desc = __FILE__ + ": Internal structure of the DList after inserting 6 strings.\n" + "Red lines are used for the list payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the list, i.e., the payload\n" + "memory WILL be deallocated by the list.\n\n" + "See dlist_mwe1i for a list example\nthat borrows the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_array_1d/Makefile b/OU1/datastructures-v2.2.2.2/src/int_array_1d/Makefile new file mode 100644 index 0000000..a0491dc --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_array_1d/Makefile @@ -0,0 +1,28 @@ +MWE = int_array_1d_mwe1 int_array_1d_mwe1i + +SRC = int_array_1d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_array_1d_mwe1: int_array_1d_mwe1.c int_array_1d.c + gcc -o $@ $(CFLAGS) $^ + +int_array_1d_mwe1i: int_array_1d_mwe1i.c int_array_1d.c + gcc -o $@ $(CFLAGS) $^ + +memtest: int_array_1d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d.c b/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d.c new file mode 100644 index 0000000..9c606fe --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d.c @@ -0,0 +1,424 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added print_internal. + */ + +// =========== INTERNAL DATA TYPES ============ + +struct int_array_1d { + int low; // Low index limit. + int high; // High index limit. + int array_size; // Number of array elements. + int *values; // Pointer to where the actual values are stored. +}; + +// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * int_array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +int_array_1d *int_array_1d_create(int lo, int hi) +{ + // Allocate array structure. + int_array_1d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low = lo; + a->high = hi; + + // Number of elements. + a->array_size = hi - lo + 1; + + a->values=calloc(a->array_size, sizeof(a->values[0])); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * int_array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int int_array_1d_low(const int_array_1d *a) +{ + return a->low; +} + +/** + * int_array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int int_array_1d_high(const int_array_1d *a) +{ + return a->high; +} + +/** + * int_array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +int int_array_1d_inspect_value(const int_array_1d *a, int i) +{ + int offset=i - int_array_1d_low(a); + // Return the value. + return a->values[offset]; +} + +/** + * int_array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool int_array_1d_has_value(const int_array_1d *a, int i) +{ + int offset=i - int_array_1d_low(a); + // Return true if the value is not 0. + return a->values[offset] != 0; +} + +/** + * int_array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or 0 to clear value. + * @i: index of position to modify. + * + * Returns: Nothing. + */ +void int_array_1d_set_value(int_array_1d *a, int v, int i) +{ + int offset=i - int_array_1d_low(a); + // Set value. + a->values[offset] = v; +} + +/** + * int_array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Returns: Nothing. + */ +void int_array_1d_kill(int_array_1d *a) +{ + // Free actual storage. + free(a->values); + // Free array structure. + free(a); +} + +/** + * int_array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void int_array_1d_print(const int_array_1d *a) +{ + printf("[ "); + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + if (int_array_1d_has_value(a, i)) { + printf("[%d]", int_array_1d_inspect_value(a, i)); + } else { + printf(" []"); + } + if (i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const int_array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"low\\n%d|high\\n%d" + "|array_size\\n%d|values\\n%04lx\"]\n", PTR2ADDR(a), + a->low, a->high, a->array_size, PTR2ADDR(a->values)); +} + +/** + * print_values() - Print a node containing all positions in the array. + * @indent_level: Indentation level. + * @a: Array to inspect. + * @max_values_to_print: Maximum number of values to output. + * + * Will print dot code to display each value in the array, up to + * max_values_to_print. If there are more values to print, adds an + * ellipsis (...) at the end. + * + * Returns: Nothing. + */ +void print_values(int indent_level, const int_array_1d *a, int max_values_to_print) +{ + int values_to_print = a->array_size; + int truncated = false; + if (values_to_print > max_values_to_print) { + values_to_print = max_values_to_print; + truncated = true; + } + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int i=0; i < values_to_print; i++) { + printf("%d\\n%02d\\n%d", i + a->low, i, a->values[i]); + if (i < values_to_print - 1) { + printf("|"); + } + } + if (truncated) { + // Add ellipsis + printf("|\\n...\\n"); + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const int_array_1d *a) +{ + print_edge(indent_level, a, a->values, "v", "values", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * int_array_1d_print_internal() - Print the arrays internal structure in dot format. + * @a: Array to inspect. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_1D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_int_array_1d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_int_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array. Limit output to 20 elements. + print_values(il, a, 20); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d_mwe1.c b/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d_mwe1.c new file mode 100644 index 0000000..8f37f94 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d_mwe1.c @@ -0,0 +1,38 @@ +#include +#include + +/* + * Minimum working example for int_array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-01-14" + +int main(void) +{ + printf("%s, %s %s: Create typed integer array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + int_array_1d *a = int_array_1d_create(1, 6); + + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + // Store square of index. + int_array_1d_set_value(a, i*i, i); + // Print array after setting each value. + printf("After setting value at index %d:\n", i); + int_array_1d_print(a); + } + + int_array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d_mwe1i.c new file mode 100644 index 0000000..44c0f9a --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_array_1d/int_array_1d_mwe1i.c @@ -0,0 +1,83 @@ +#include +#include + +/* + * Minimum working example for int_array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-08: Adapted from int_array_1d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-08" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const int_array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + int_array_1d_print_internal(a, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + int_array_1d *a = int_array_1d_create(1, 6); + + printf("Empty array from the outside:\n"); + int_array_1d_print(a); + print_internal_with_cut_lines(a, "Empty array"); + + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + // Store square of index. + int_array_1d_set_value(a, i*i, i); + } + // Print array after setting all values. + printf("Full array from the outside:\n"); + int_array_1d_print(a); + print_internal_with_cut_lines(a, "Full array from the inside"); + + int_array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list/Makefile b/OU1/datastructures-v2.2.2.2/src/int_list/Makefile new file mode 100644 index 0000000..75c4e20 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list/Makefile @@ -0,0 +1,44 @@ +MWE = int_list_mwe1 int_list_mwe1i +TEST = int_list_test + +SRC = int_list.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Test programs. +test: $(TEST) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +int_list_mwe1: int_list_mwe1.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +int_list_mwe1i: int_list_mwe1i.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +int_list_test: int_list_test.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +test_run: test + # Run the test + ./$(TEST) + +memtest_mwe: int_list_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test: int_list_test + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest: memtest_mwe memtest_test diff --git a/OU1/datastructures-v2.2.2.2/src/int_list/int_list.c b/OU1/datastructures-v2.2.2.2/src/int_list/int_list.c new file mode 100644 index 0000000..3d4b645 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list/int_list.c @@ -0,0 +1,542 @@ +#include +#include +#include + +#include + +/* + * Implementation of a typed, undirected list of integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses linked + * 2-cells. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.01 2018-03-26: Bugfix: Corrected const declaration in remove. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v2.0 2024-05-10: Added print_internal. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list elements are implemented as two-cells with forward and + * backward links and place to store one integer. The list uses two + * border cells at the start and end of the list. + */ +typedef struct cell { + struct cell *next; + struct cell *prev; + int val; +} cell; + +struct list { + cell *head; + cell *tail; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void) +{ + // Allocate memory for the list head. + list *l = calloc(1, sizeof(list)); + + // Allocate memory for the border cells. + l->head = calloc(1, sizeof(cell)); + l->tail = calloc(1, sizeof(cell)); + + // Set consistent links between border elements. + l->head->next = l->tail; + l->tail->prev = l->head; + + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list * l) +{ + // List is empty if there are no cells between head and tail. + return (l->head->next == l->tail); +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list * l) +{ + // First position is position of first element. + return l->head->next; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list * l) +{ + // Last position is position *after* last element. + return l->tail; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); + } + return p->next; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p->prev; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->val; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list * l, int v, const list_pos p) +{ + // Allocate memory for a new cell. + list_pos e = malloc(sizeof(cell)); + + // Store the value. + e->val = v; + // Add links to/from the new cell. + e->next = p; + e->prev = p->prev; + p->prev = e; + e->prev->next = e; + + // Return the position of the new cell. + return e; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Remember return position. + list_pos next_pos = p->next; + // Link past this element. + p->prev->next = p->next; + p->next->prev = p->prev; + + // Free the memory allocated to the cell itself. + free(p); + // Return the position of the next element. + return next_pos; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. + * + * Returns: Nothing. + */ +void list_kill(list * l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + list_pos p = list_first(l); + + // Remove first element until list is empty. + while (!list_is_empty(l)) { + p = list_remove(l, p); + } + + // Free border elements and the list head. + free(l->head); + free(l->tail); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * + * Iterates over the list and print each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list * l) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + printf("[%d]\n", list_inspect(l, p)); + p = list_next(l, p); + } +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // Iterate over all positions in l. + list_pos q = list_first(l); + while (!list_pos_is_equal(l, q, list_end(l))) { + if (list_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = list_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY int_list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->head), PTR2ADDR(l->tail), PTR2ADDR(l)); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edges(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); + print_edge(indent_level, l, l->tail, "t", "tail", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const list_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%d|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), p->val, PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const list *l, const list_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + print_edge(indent_level, p, p->prev, "p", "prev", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + list_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list/int_list_mwe1.c b/OU1/datastructures-v2.2.2.2/src/int_list/int_list_mwe1.c new file mode 100644 index 0000000..0847169 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list/int_list_mwe1.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l); + + // Insert the value 2 at the second position in the list. + list_insert(l, 2, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list/int_list_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/int_list/int_list_mwe1i.c new file mode 100644 index 0000000..4b67328 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list/int_list_mwe1i.c @@ -0,0 +1,97 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + printf("Empty list from the outside.\n"); + list_print(l); + print_internal_with_cut_lines(l, "Empty list showing the internals"); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List from the outside after inserting one value:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting one value showing the internals"); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List from the outside after inserting second value at the end:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals"); + + printf("List after removing first element:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after removing first element showing the internals"); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list/int_list_test.c b/OU1/datastructures-v2.2.2.2/src/int_list/int_list_test.c new file mode 100644 index 0000000..2ffde69 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list/int_list_test.c @@ -0,0 +1,1101 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create an empty list + list *l = list_empty(); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create an empty list + list *l = list_empty(); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create an empty list + list *l = list_empty(); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(); + // Insert an element at the only position in the empty list + list_insert(l, v, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(); + // Insert the first element + list_pos p = list_insert(l, v1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, v2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, 24, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int read_value = list_inspect(l, list_first(l)); + + if (!value_equal(read_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", read_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Insert new element at end + list_insert(l, v[1], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Insert new element at end + list_insert(l, v[2], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Insert new element at next(first()) + list_insert(l, v[1], list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the end of the list + list_insert(l, n, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (!value_equal(i + 1, list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the beginning of the list + list_insert(l, n, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (!value_equal(n - i, list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_list datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list_array/Makefile b/OU1/datastructures-v2.2.2.2/src/int_list_array/Makefile new file mode 100644 index 0000000..9cfd2b0 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list_array/Makefile @@ -0,0 +1,44 @@ +MWE = int_list_array_mwe1 int_list_array_mwe1i +TEST = int_list_array_test + +SRC = int_list_array.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Test programs. +test: $(TEST) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +int_list_array_mwe1: int_list_array_mwe1.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +int_list_array_mwe1i: int_list_array_mwe1i.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +int_list_array_test: int_list_array_test.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +test_run: test + # Run the test + ./$(TEST) + +memtest_mwe1: int_list_array_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test: int_list_array_test + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest: memtest_mwe1 memtest_test diff --git a/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array.c b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array.c new file mode 100644 index 0000000..a54e1b5 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array.c @@ -0,0 +1,515 @@ +#include +#include +#include // For bcopy +#include + +#include + +/* + * Implementation of a typed, undirected list of integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * static array. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. Bugfix + * in list_remove. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list is implemented as a static array. + */ +struct list { + int last_used_pos; + int *values; + int array_size; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +#define ARRAY_MAX_SIZE 100 + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void) +{ + // Allocate memory for the list head. + list *l=malloc(sizeof(list)); + // Set the size + l->array_size = ARRAY_MAX_SIZE; + // Allocate memory for the elements. + l->values=calloc(l->array_size,sizeof(l->values[0])); + // Set last used position. + l->last_used_pos=-1; + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list * l) +{ + // List is empty if no elements are used. + return l->last_used_pos < 0; +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list * l) +{ + // First position is always 0. + return 0; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list * l) +{ + // Last position is position *after* last used element. + return l->last_used_pos + 1; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate " + "past end of list!"); + } + return p + 1; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p - 1; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return l->values[p]; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list * l, int v, const list_pos p) +{ + // Move elements at position pos and later forward. + bcopy(l->values + p, l->values + p + 1, + sizeof(int) * (l->last_used_pos - p + 1)); + + // Set value. + l->values[p] = v; + + // Increment number of used elements. + l->last_used_pos++; + + // Return the position of the new value. + return p; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Move elements at position pos and later forward. + bcopy(l->values + p + 1, l->values + p, sizeof(int) * (l->last_used_pos - p)); + + // Decrement number of used elements. + l->last_used_pos--; + + // p now refers to the position after the removed element. + return p; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. + * + * Returns: Nothing. + */ +void list_kill(list * l) +{ + free(l->values); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * + * Iterates over the list and print each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list * l) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + printf("[%d]\n",list_inspect(l, p)); + p = list_next(l, p); + } +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // The position is valid if it is between 0 and last_used_pos + 1, inclusive. + return p >= 0 && p <= l->last_used_pos+1; + +} + +// ===========INTERNAL FUNCTIONS USED BY int_list_array_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"last_used_pos\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(l), l->last_used_pos, l->array_size, PTR2ADDR(l->values)); +} + +/** + * print_values() - Print a node containing all positions in the array. + * @indent_level: Indentation level. + * @l: List to inspect. + * @max_values_to_print: Maximum number of values to output. + * + * Will print dot code to display each value in the array, up to + * max_values_to_print. If there are more values to print, adds an + * ellipsis (...) at the end. + * + * Returns: Nothing. + */ +void print_values(int indent_level, const list *l, int max_values_to_print) +{ + int values_to_print = l->array_size; + int truncated = false; + if (values_to_print > max_values_to_print) { + values_to_print = max_values_to_print; + truncated = true; + } + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(l->values)); + for (int i=0; i < values_to_print; i++) { + printf("%02d\\n%d", i, l->values[i]); + if (i < values_to_print - 1) { + printf("|"); + } + } + if (truncated) { + // Add ellipsis + printf("|\\n...\\n"); + } + printf("\"]\n"); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edge(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->values, "v", "values", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the List content + iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the values array. Limit the output to 10 elements. + print_values(il, l, 10); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, l); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_mwe1.c b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_mwe1.c new file mode 100644 index 0000000..d222f84 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_mwe1.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +int main(void) +{ + printf("%s, %s %s: Create typed integer list, implemented as an array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l); + + // Insert the value 2 at the second position in the list. + list_insert(l, 2, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_mwe1i.c new file mode 100644 index 0000000..4b67328 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_mwe1i.c @@ -0,0 +1,97 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + printf("Empty list from the outside.\n"); + list_print(l); + print_internal_with_cut_lines(l, "Empty list showing the internals"); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List from the outside after inserting one value:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting one value showing the internals"); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List from the outside after inserting second value at the end:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals"); + + printf("List after removing first element:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after removing first element showing the internals"); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_test.c b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_test.c new file mode 100644 index 0000000..cf7fe4c --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_list_array/int_list_array_test.c @@ -0,0 +1,1145 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2023-01-21: v1.0. First public version. This is effectively a copy of the int_list_test.c + * file. + * 2023-03-23: v1.1. Renamed list_pos_are_equal to list_pos_is_equal. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Run the code + list *l = list_empty(); + + // l should be non-NULL + if (l != NULL) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Run the code + list *l = list_empty(); + + // The list returned by empty() should be is_empty() + if (list_is_empty(l) == true) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Run the code + list *l = list_empty(); + + // first(l) should be == end(l) for an empty list + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(); + // Insert an element at the only position in the empty list + list_insert(l, v, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(); + // Insert the first element + list_pos p = list_insert(l, v1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, v2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (!list_is_empty(l)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (!list_pos_is_equal(l, list_first(l), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, 24, list_first(l)); + + // The returned position should be first in the modified list + if (list_pos_is_equal(l, p, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int read_value = list_inspect(l, list_first(l)); + + if (value_equal(read_value, val)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", read_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (!list_pos_is_equal(l, list_next(l, p), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (!list_pos_is_equal(l, list_prev(l, p), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (prev_is_inv_next(l, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (next_is_inv_prev(l, list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that next is inverse of prev on end() + if (list_is_empty(l)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (list_pos_is_equal(l, list_end(l), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_first(l)); + + // Returned position should be == first(l) + if (list_pos_is_equal(l, p, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_end(l)); + + // Returned position should be == prev(end(l)) + if (list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (prev_is_inv_next(l, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (next_is_inv_prev(l, list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Insert new element at end + list_insert(l, v[1], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Insert new element at end + list_insert(l, v[2], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Insert new element at next(first()) + list_insert(l, v[1], list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the end of the list + list_insert(l, n, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (value_equal(i + 1, list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", i + 1, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the beginning of the list + list_insert(l, n, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (value_equal(n - i, list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + n - i, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_list_array datatype.\n", __FILE__, + VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/.gitignore b/OU1/datastructures-v2.2.2.2/src/int_queue/.gitignore new file mode 100644 index 0000000..824be8c --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/.gitignore @@ -0,0 +1,2 @@ +int_queue_example_internal +int_queue_example diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/Makefile b/OU1/datastructures-v2.2.2.2/src/int_queue/Makefile new file mode 100644 index 0000000..44a2320 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/Makefile @@ -0,0 +1,34 @@ +MWE = int_queue_mwe1 int_queue_mwe1i int_queue_example int_queue_example_internal + +SRC = int_queue.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_queue_mwe1: int_queue_mwe1.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_mwe1i: int_queue_mwe1i.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_example: int_queue_example.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_example_internal: int_queue_example_internal.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: int_queue_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue.c b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue.c new file mode 100644 index 0000000..51e521f --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue.c @@ -0,0 +1,310 @@ +#include +#include +#include + +#include + +/* + * Implementation of an integer queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores integers directly and does not use + * dynamic memory. Thus, the clean-up function queue_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic queue implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +/** + * queue_empty() - Create an empty queue. + * + * Returns: A new, empty, queue. + */ +queue queue_empty(void) +{ + queue q; + q.first_free_pos = 0; + // Set all elements to zero. Not really needed, but otherwise + // the output from print_internal becomes unreadable. + for (int i = 0; i < sizeof(q.elements)/sizeof(q.elements[0]); i++) { + q.elements[i] = 0; + } + return q; +} + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue q) +{ + // The queue is empty if no positions are occupied with elements, + // i.e., the first free position is zero + return q.first_free_pos == 0; +} + +/** + * queue_enqueue() - Put a value at the end of a queue. + * @q: Queue to manipulate. + * @v: Value (integer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue queue_enqueue(queue q, int v) +{ + // Store value at first free position. + q.elements[q.first_free_pos] = v; + // Update first free position. + q.first_free_pos++; + + // Return the new queue. + return q; +} + +/** + * queue_dequeue() - Remove the element at the beginning of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue queue_dequeue(queue q) +{ + if (queue_is_empty(q)) { + fprintf(stderr, "queue_dequeue: Warning: dequeue on empty queue\n"); + } else { + // Shift all elements one step to the left + for (int i = 0; i < q.first_free_pos - 1; i++) { + q.elements[i] = q.elements[i + 1]; + } + // Update the position of the first free element + q.first_free_pos--; + } + return q; +} + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the front of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +int queue_front(const queue q) +{ + if (queue_is_empty(q)) { + fprintf(stderr, "queue_front: Warning: front on empty queue\n"); + } + return q.elements[0]; +} + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Does nothing since the queue does not use any dynamic + * memory. Included for symmetry with generic queue.h. + * + * Returns: Nothing. + */ +void queue_kill(queue q) +{ + // Do nothing. +} + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * + * Iterates over the queue and prints each integer. + * + * Returns: Nothing. + */ +void queue_print(const queue q) +{ + printf("{ "); + // Print elements from the top down. + for (int i = 0; i < q.first_free_pos; i++) { + printf("[%d]", q.elements[i]); + if (i < q.first_free_pos - 1) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i q [style=invis] " + "// Dummy line to place description above\n"); +} + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the queue and outputs dot code that shows the + * internal structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph QUEUE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + // Output the head node + print_head_node(il, max_elems, q); + + // Output a dummy line to place the description at the top + print_desc_edge(il); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_example.c b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_example.c new file mode 100644 index 0000000..1a3416e --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_example.c @@ -0,0 +1,50 @@ +#include +#include + +#include + +/* + * A small example that uses an integer queue. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-16: First public version. + */ + +int main(void) +{ + // Create the queue. + queue q = queue_empty(); + + // The queue should be empty. Is it? + if (queue_is_empty(q)) { + printf("The newly created queue is empty.\n"); + } else { + printf("The newly created queue is NOT empty.\n"); + } + + for (int i = 11; i <= 13; i++) { + printf("Add the value %d at the end of the queue.\n", i); + // Put some values in the queue. + q = queue_enqueue(q, i); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + // Remove one element from the queue. + printf("Remove one element.\n"); + q = queue_dequeue(q); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_example_internal.c b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_example_internal.c new file mode 100644 index 0000000..b4110fb --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_example_internal.c @@ -0,0 +1,99 @@ +#include +#include + +#include + +// Size of internal string buffer +#define BUFLEN 400 + +/* + * A small example that uses an integer queue. + * + * Generates two kinds of output: + * 1) The 'outside' view showing the stored values only. + * 2) The 'inside' view showing the internal organization. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-24: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-24" + +// Print a message, then the queue and wait for the user to press return. +void print_and_wait(const queue q, const char *msg) +{ + printf("\nThe output from queue_print() shows the values stored in the queue\n"); + printf("\n%s\n", msg); + queue_print(q); + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_and_wait(queue q, const char *desc) +{ + // Print starting marker line. + printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n"); + printf("\n\n1) Copy the lines between the cut marks\n" + "2) Paste into the left half of the browser window.\n" + "3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n"); + printf("\n--- CUT HERE ---\n\n"); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, desc, 0, 10); + + // Print ending marker line + printf("\n--- END CUT HERE ---\n\n"); + + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +int main(void) +{ + // Message buffer + char buf[BUFLEN]; + + printf("%s, %s %s: Example use of a typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue q = queue_empty(); + + // Call print functions to show the content of the queue + print_and_wait(q, "The queue is empty"); + print_internal_and_wait(q, "The queue is empty"); + + for (int i = 11; i <= 13; i++) { + q = queue_enqueue(q, i); + snprintf(buf, BUFLEN, "The value %d was added to the queue", i); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + // Remove one element from the queue. + q = queue_dequeue(q); + snprintf(buf, BUFLEN, "The front element was removed from the queue"); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_mwe1.c b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_mwe1.c new file mode 100644 index 0000000..8a5b416 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_mwe1.c @@ -0,0 +1,56 @@ +#include +#include + +#include + +/* + * Minimum working example for int_queue.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-10" + +int main(void) +{ + printf("%s, %s %s: Create typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue q = queue_empty(); + printf("--Empty QUEUE--\n"); + queue_print(q); + + + for (int i = 11; i <= 13; i++) { + // Put some values in the queue. + q = queue_enqueue(q, i); + printf("--QUEUE during insert--\n"); + queue_print(q); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + printf("--QUEUE after enqueue, before dequeueing--\n"); + queue_print(q); + + // Remove one element from the queue. + q = queue_dequeue(q); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_mwe1i.c new file mode 100644 index 0000000..2a8c57b --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_queue/int_queue_mwe1i.c @@ -0,0 +1,104 @@ +#include +#include + +#include + +/* + * Minimum working example for int_queue.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: Adapted from int_queue_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-10" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(queue s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(s, desc, 0, 10); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue q=queue_empty(); + + printf("Empty queue from the outside:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Empty queue showing the inside"); + + for (int i = 11; i <= 13; i++) { + // Put values in the queue. + q = queue_enqueue(q, i); + } + + printf("Queue for the outside after adding 3 elements:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after adding 3 elements"); + + // Remove one element from queue. + q = queue_dequeue(q); + + printf("Queue for the outside after removing 1 element:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after removing 1 element"); + + // Pop remaining elements + while (!queue_is_empty(q)) { + q = queue_dequeue(q); + } + + printf("Queue from the outside after removing all elements:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after removing all elements"); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue_mwe1.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_stack/Makefile b/OU1/datastructures-v2.2.2.2/src/int_stack/Makefile new file mode 100644 index 0000000..9611338 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_stack/Makefile @@ -0,0 +1,28 @@ +MWE = int_stack_mwe1 int_stack_mwe1i + +SRC = int_stack.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_stack_mwe1: int_stack_mwe1.c int_stack.c + gcc -o $@ $(CFLAGS) $^ + +int_stack_mwe1i: int_stack_mwe1i.c int_stack.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: int_stack_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack.c b/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack.c new file mode 100644 index 0000000..0012c3d --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack.c @@ -0,0 +1,316 @@ +#include +#include +#include + +#include + +/* + * Implementation of an integer stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores integers directly and does not use + * dynamic memory. Thus, the clean-up function stack_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic stack implementations. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se) + * Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2022-03-24: First public version. + * v1.1 2024-04-10: Added print_internal. + * v1.2 2024-05-10: Added/updated print_internal with improved encapsulation. + */ + +/** + * stack_empty() - Create an empty stack. + * + * Returns: An empty stack. + */ +stack stack_empty(void) +{ + stack s; + s.first_free_pos = 0; + // Set all elements to zero. Not really needed, but otherwise + // the output from print_internal becomes unreadable. + for (int i = 0; i < sizeof(s.elements)/sizeof(s.elements[0]); i++) { + s.elements[i] = 0; + } + return s; +} + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack s) +{ + // The stack is empty if no positions are occupied with elements, + // i.e., the first free position is zero + return s.first_free_pos == 0; +} + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (integer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_push(stack s, int v) +{ + // Store value at first free position. + s.elements[s.first_free_pos] = v; + // Update first free position. + s.first_free_pos++; + + // Return the new stack. + return s; +} + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_pop(stack s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_pop: Warning: pop on empty stack\n"); + } else { + // We only have to decrease the first free position to + // indicate that the element that used to be on top of the + // stack is now free for use. + s.first_free_pos--; + } + return s; +} + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The integer at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +int stack_top(const stack s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_top: Warning: top on empty stack\n"); + } + return s.elements[s.first_free_pos - 1]; +} + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Does nothing since the stack does not use any dynamic + * memory. Included for symmetry with generic stack.h. + * + * Returns: Nothing. + */ +void stack_kill(stack s) +{ + // Do nothing. +} + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * + * Iterates over the stack and prints each integer. + * + * Returns: Nothing. + */ +void stack_print(const stack s) +{ + printf("{ "); + // Print elements from the top down. + for (int i = s.first_free_pos - 1; i >= 0; i--) { + printf("[%d]", s.elements[i]); + if (i>0) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i s [style=invis] " + "// Dummy line to place description above\n"); +} + +/** + * stack_print_internal() - Print the internal structure of the stack in dot format. + * @s: Stack to inspect. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the stack and outputs dot code that shows the + * internal structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph STACK_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + // Output the head node + print_head_node(il, max_elems, s); + + // Output a dummy line to place the description at the top + print_desc_edge(il); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack_mwe1.c b/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack_mwe1.c new file mode 100644 index 0000000..cd5039d --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack_mwe1.c @@ -0,0 +1,48 @@ +#include +#include + +#include + +/* + * Minimum working example for int_stack.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2022-03-24: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-01-14" + +int main(void) +{ + printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. + stack s=stack_empty(); + + for (int i=1; i<=3; i++) { + // Push value on stack. + s = stack_push(s, i); + } + + printf("--STACK before popping--\n"); + stack_print(s); + + // Pop element from stack. + s=stack_pop(s); + + printf("--STACK after popping--\n"); + stack_print(s); + + // Kill stack. Strictly not necessary for int_stack since it + // does not use any dynamic memory. Included for symmetry with + // stack.c. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack_mwe1i.c new file mode 100644 index 0000000..dcd4c97 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack_mwe1i.c @@ -0,0 +1,103 @@ +#include +#include + +#include + +/* + * Minimum working example for int_stack.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-10: Adapted from int_stack_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-10" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(stack s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, desc, 0, 10); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack s=stack_empty(); + + printf("Empty stack from the outside:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Empty stack showing the inside"); + + for (int i=1; i<=3; i++) { + // Push value on stack. + s = stack_push(s, 10 + i); + } + + printf("Stack for the outside after pushing 3 elements:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after pushing 3 elements"); + + // Pop one element from stack. + s = stack_pop(s); + + printf("Stack for the outside after popping 1 element:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after popping 1 element"); + + // Pop remaining elements + while (!stack_is_empty(s)) { + s = stack_pop(s); + } + + printf("Stack from the outside after popping all elements:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after popping all elements"); + + // Kill stack. Strictly not necessary for int_stack since it + // does not use any dynamic memory. Included for symmetry with + // stack_mwe1.c. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/Makefile b/OU1/datastructures-v2.2.2.2/src/list/Makefile new file mode 100644 index 0000000..ea97d56 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/Makefile @@ -0,0 +1,68 @@ +MWE = list_mwe1 list_mwe2 list_mwe1i list_mwe2i +TEST = list_test1 list_test2 + +SRC = list.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +test: list_test1 list_test2 + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +list_mwe1: list_mwe1.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe2: list_mwe2.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe1i: list_mwe1i.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe2i: list_mwe2i.c list.c + gcc -o $@ $(CFLAGS) $^ + +memtest_mwe1: list_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe2: list_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe1i: list_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe2i: list_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< + +list_test1: list_test1.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_test2: list_test2.c list.c + gcc -o $@ $(CFLAGS) $^ + +test_run1: list_test1 + # Run the test + ./$< + +test_run2: list_test2 + # Run the test + ./$< + +memtest_test1: list_test1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test2: list_test2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtests: memtest_mwe1 memtest_mwe2 memtest_mwe1i memtest_mwe2i memtest_test1 memtest_test2 diff --git a/OU1/datastructures-v2.2.2.2/src/list/README.md b/OU1/datastructures-v2.2.2.2/src/list/README.md new file mode 100644 index 0000000..3d203fd --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/README.md @@ -0,0 +1,111 @@ +# Dubbellänkad lista +En två-cellsimplementation av ADT:n _Lista_. + +## Minneshantering + +Följande kodsnutt kommer skapa en lista med ett element som har värdet 5. I +detta exempel så allokeras inget minne dynamiskt, därför behöver vi inte heller +ta hand om något minne efter vi använt klart listan. + +```c +int val = 5; +list *l = list_empty(NULL); +list_insert(l, &val, list_first(l)); +``` + +Vi kan skriva om ovan exempel så att minne allokeras på heapen, men då måste vi +manuellt komma ihåg att avallokera detta minne efter användning. + +```c +int *val = malloc(sizeof(int)); +*val = 5; + +list *l = list_empty(NULL); +list_insert(l, val, list_first(l)); +... +list_kill(l); +free(val); +``` + +Om vi vill allokera minne dynamiskt, men vill att listan själva ska hantera +avallokering av minne (e.g. vid `list_remove`) så behöver vi ange en +minneshanteringsfunktion: +```c +int *val = malloc(sizeof(int)); +*val = 5; + +list *l = list_empty(free); +list_insert(l, val, list_first(l)); +... +list_kill(l); +``` + +I exemplet ovan kommer `list_kill(l)` att anropa `free` för varje element i +listan, och därmed sköta minneshanteringen åt oss. + +### Egen free-funktion + +Det är också möjligt att definiera en egen funktion som ska sköta +avallokeringen. Till exempel kanske vi vill skriva ut varje värde som +avallokeras i debugging-syfte. Då kan vi skriva en enkel wrapper-funktion för +`free` på följande sätt: + +```c +static void free_and_print(void *data) +{ + printf("Free'ing [%d]\n", *(int*)data); + free(data); +} +``` + +Denna funktion skulle då kunna användas på följande sätt: + +```c +list *l = list_empty(free_and_print); +int *val = malloc(sizeof(int)); +*val = 5; +list_insert(l, val, list_first(l)); +list_kill(l); +``` + +Denna kodsnutt skulle då skriva ut `Free'ing [5]`. + + +## Utskrift + +För att skriva ut listan, t.ex. vid debugging eller för att visa innehållet för +en användare så kan vi definiera en egen funktion för att presentera tillståndet +hos datatypen. Det enklaste fallet är om vi enbart vill skriva ut, e.g., en +lista med heltal där varje värde hamnar på en egen rad: + +```c +static void print_ints(const void *data) +{ + printf("%d\n", *(int*)data); +} +``` + +Notera att inparametern är av typen `const`. `const` indikerar att pekaren bara +får läsas, ej manipuleras. Vi vill inte att en print-funktion ska kunna ändra på +listans innehåll. + +`print_ints` kan nu användas på följande sätt: + +```c +list *l = list_empty(free); +int *val = malloc(sizeof(int)); +*val = 5; +list_insert(l, val, list_first(l)); + +list_print(l, print_ints); +``` + +Detta exempel skulle skriva ut `5` på en egen rad. + + +# Minimal working example + +Mycket av det som behandlats ovan sammanfattas i följande minimal working +example: + +Se [list_mwe1.c](list_mwe1.c) och [list_mwe2.c](list_mwe2.c). diff --git a/OU1/datastructures-v2.2.2.2/src/list/list.c b/OU1/datastructures-v2.2.2.2/src/list/list.c new file mode 100644 index 0000000..a09391a --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list.c @@ -0,0 +1,620 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic, undirected list for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-02-25: Renamed list header fields head and tail. + * v1.4 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-03-14: Added list_print_internal to output dot code for visualization. + * v2.1 2024-05-10: Added/updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +/* + * The list elements are implemented as two-cells with forward and + * backward links and a void * for the value. The list uses two border + * cells at the start and end of the list. + */ +typedef struct cell { + struct cell *next; + struct cell *prev; + void *val; +} cell; + +struct list { + cell *head; + cell *tail; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * list_empty() - Create an empty list. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +list *list_empty(kill_function kill_func) +{ + // Allocate memory for the list head. + list *l = calloc(1, sizeof(list)); + + // Allocate memory for the border cells. + l->head = calloc(1, sizeof(cell)); + l->tail = calloc(1, sizeof(cell)); + + // Set consistent links between border elements. + l->head->next = l->tail; + l->tail->prev = l->head; + + // Store the kill function. + l->kill_func = kill_func; + + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l) +{ + // List is empty if there are no cells between head and tail. + return (l->head->next == l->tail); +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l) +{ + // First position is position of first element. + return l->head->next; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l) +{ + // Last position is position *after* last element. + return l->tail; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); + } + return p->next; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p->prev; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *list_inspect(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->val; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, void *v, const list_pos p) +{ + // Allocate memory for a new cell. + list_pos e = malloc(sizeof(cell)); + + // Store the value. + e->val = v; + // Add links to/from the new cell. + e->next = p; + e->prev = p->prev; + p->prev = e; + e->prev->next = e; + + // Return the position of the new cell. + return e; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Remember return position. + list_pos next_pos = p->next; + // Link past this element. + p->prev->next = p->next; + p->next->prev = p->prev; + + // Call kill_func if registered. + if (l->kill_func != NULL) { + // Return any user-allocated memory for the value. + l->kill_func(p->val); + } + // Return the memory allocated to the cell itself. + free(p); + // Return the position of the next element. + return next_pos; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element + * values. + * + * Returns: Nothing. + */ +void list_kill(list *l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + list_pos p = list_first(l); + + // Remove first element until list is empty. + while (!list_is_empty(l)) { + p = list_remove(l, p); + } + + // Free border elements and the list head. + free(l->head); + free(l->tail); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void list_print(const list *l, inspect_callback print_func) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + printf("( "); + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + print_func(list_inspect(l, p)); + // Advance to next position. + p = list_next(l, p); + // Print separator unless at end. + if (!list_pos_is_equal(l, p, list_end(l))) { + printf(", "); + } + } + printf(" )\n"); +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // Iterate over all positions in l. + list_pos q = list_first(l); + while (!list_pos_is_equal(l, q, list_end(l))) { + if (list_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = list_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l->tail), + PTR2ADDR(l)); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edges(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); + print_edge(indent_level, l, l->tail, "t", "tail", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const list_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%04lx|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const list *l, const list_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + print_edge(indent_level, p, p->prev, "p", "prev", NULL); + + // Ignore val ptr for head and tail nodes. + if (p == l->head || p == l->tail) { + return; + } + if (l->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + list_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = l->head; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/list_mwe1.c b/OU1/datastructures-v2.2.2.2/src/list/list_mwe1.c new file mode 100644 index 0000000..8d7d20d --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list_mwe1.c @@ -0,0 +1,103 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +static void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer list without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(NULL); + + // Create a dynamic integer with value 5. + int *val = int_create(5); + + // Insert the value first in the list. + list_insert(l, val, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l, print_int); + + // Allocate space for another integer. + val = int_create(8); + // Insert the value last in the list. + list_insert(l, val, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l, print_int); + + // Allocate space for a third integer. + val = int_create(2); + // Insert the value at the second position in the list. + list_insert(l, val, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l, print_int); + + // Remove first element. + list_pos p=list_first(l); + int *v=list_inspect(l, p); + // Remove element from list. + list_remove(l, p); + // Free element content. + int_kill(v); + + printf("List after removing first element:\n"); + list_print(l, print_int); + + // Empty the list. + while (!list_is_empty(l)) { + list_pos p=list_first(l); + int *v=list_inspect(l,p); + int_kill(v); + list_remove(l,p); + } + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/list_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/list/list_mwe1i.c new file mode 100644 index 0000000..e350614 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list_mwe1i.c @@ -0,0 +1,131 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "borrows" the payload memory, i.e., the user of the list is + * responsible for deallocating the payload memory. See list_mwe2i.c + * for a version where the list takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer list without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the list. + list *l = list_empty(NULL); + + printf("Empty list from the outside:\n"); + list_print(l, print_int); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + // Insert the value 5 first in the list. + list_insert(l, int_create(5), list_first(l)); + // Insert the value 8 last in the list. + list_insert(l, int_create(8), list_end(l)); + // Insert the value 2 at the second position in the list. + list_insert(l, int_create(2), list_next(l, list_first(l))); + + printf("List from the outside after inserting 3 values:\n"); + list_print(l, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the List after inserting 3 values.\n" + "Red lines are used for the list payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the list, i.e., the payload\n" + "memory will NOT be deallocated by the list.\n\n" + "See list_mwe2i for a list example\nthat owns the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Empty the list. + while (!list_is_empty(l)) { + list_pos p=list_first(l); + int *v=list_inspect(l,p); + int_kill(v); + list_remove(l,p); + } + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/list_mwe2.c b/OU1/datastructures-v2.2.2.2/src/list/list_mwe2.c new file mode 100644 index 0000000..0820443 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list_mwe2.c @@ -0,0 +1,92 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.21 2024-01-16: Fix include to be with brackets, not citation marks. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +static void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer list with custom kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(int_kill); + + // Create a dynamic integer with value 5. + int *val = int_create(5); + + // Insert the value first in the list. + list_insert(l, val, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l, print_int); + + // Allocate space for another integer. + val = int_create(8); + // Insert the value last in the list. + list_insert(l, val, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l, print_int); + + // Allocate space for a third integer. + val = int_create(2); + // Insert the value at the second position in the list. + list_insert(l, val, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l, print_int); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l, print_int); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/list_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/list/list_mwe2i.c new file mode 100644 index 0000000..8866d32 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list_mwe2i.c @@ -0,0 +1,125 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "owns" the payload memory, i.e., the list takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See list_mwe1i.c for a version + * where the list does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer list with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the list. + list *l = list_empty(int_kill); + + printf("Empty list from the outside:\n"); + list_print(l, print_int); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + // Insert the value 5 first in the list. + list_insert(l, int_create(5), list_first(l)); + // Insert the value 8 last in the list. + list_insert(l, int_create(8), list_end(l)); + // Insert the value 2 at the second position in the list. + list_insert(l, int_create(2), list_next(l, list_first(l))); + + printf("List from the outside after inserting 3 values:\n"); + list_print(l, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the List after inserting 3 values.\n" + "Red lines are used for the list payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the list, i.e., the payload\n" + "memory WILL be deallocated by the list.\n\n" + "See list_mwe1i for a list example\nthat borrows the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Kill the list, including the payload. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/list_test1.c b/OU1/datastructures-v2.2.2.2/src/list/list_test1.c new file mode 100644 index 0000000..ea4e0dc --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list_test1.c @@ -0,0 +1,1190 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Test program for the list implementation in list.c. This version + * uses a free-handler, i.e., the list will be responsible for + * deallocating any elements we allocate and put in it. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * v1.0 2023-01-21: First public version. + * v1.1 2023-03-21: Removed empty if statements in test code. + * v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(int_kill); + // Make a dynamic copy of v. + int *q = int_create(v); + // Insert an element at the only position in the empty list + list_insert(l, q, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(int_kill); + // Make dynamic copies of v1 and v2 + int *q1 = int_create(v1); + int *q2 = int_create(v2); + // Insert the first element + list_pos p = list_insert(l, q1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, q2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(int_kill); + // Create a dynamic integer + int *q = int_create(24); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, q, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int *stored_ptr = list_inspect(l, list_first(l)); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of element value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Make dynamic copy of element to insert + int *q1 = int_create(v[1]); + + // Insert new element at end + list_insert(l, q1, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Make dynamic copy of element to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Make dynamic copy of value to insert + int *q2 = int_create(v[2]); + + // Insert new element at end + list_insert(l, q2, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Make dynamic copy of value to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Make dynamic copy of value to insert + int *q1 = int_create(v[1]); + + // Insert new element at next(first()) + list_insert(l, q1, list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(int_kill); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the end of the list + list_insert(l, q, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(i + 1, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(int_kill); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the beginning of the list + list_insert(l, q, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(n - i, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(int_kill); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(int_kill); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(int_kill); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(int_kill); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the generic list.\n" + "Uses the standard free-handler to handle deallocation of dynamic memory.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Uses code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/list/list_test2.c b/OU1/datastructures-v2.2.2.2/src/list/list_test2.c new file mode 100644 index 0000000..7bfcd8e --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/list/list_test2.c @@ -0,0 +1,1308 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Test program for the list implementation in list.c. This version + * does not use a free-handler, i.e., the list will not be responsible + * for deallocating any allocated elements stored in it. Instead, the + * responsibility remains with the user of the list. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * v1.0 2023-01-21: First public version. + * v1.1 2023-03-21: Removed empty if statements in test code. + * v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * kill_all_element_values() - kill all element values stored in the list + * @l - List to operate on + * + * Returns: A pointer to the modified list + * + * The function traverses the list and calls int_kill for the value (void *) + * stored in each element. WARNING: This should only be executed + * immediately before the list is killed since all element values are + * UNDEFINED after the call. + */ +list *kill_all_element_values(list *l) +{ + // Iterate over all elements + list_pos p=list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + // Read the stored pointer value + void *q = list_inspect(l, p); + // Free its memory + int_kill(q); + // Advance to the next element + p = list_next(l, p); + } + return l; +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(NULL); + // Make a dynamic copy of v. + int *q = int_create(v); + // Insert an element at the only position in the empty list + list_insert(l, q, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(NULL); + // Make dynamic copies of v1 and v2 + int *q1 = int_create(v1); + int *q2 = int_create(v2); + // Insert the first element + list_pos p = list_insert(l, q1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, q2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(NULL); + // Create a dynamic integer + int *q = int_create(24); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, q, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int *stored_ptr = list_inspect(l, list_first(l)); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of element value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Make dynamic copy of element to insert + int *q1 = int_create(v[1]); + + // Insert new element at end + list_insert(l, q1, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Make dynamic copy of element to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Make dynamic copy of value to insert + int *q2 = int_create(v[2]); + + // Insert new element at end + list_insert(l, q2, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Make dynamic copy of value to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Make dynamic copy of value to insert + int *q1 = int_create(v[1]); + + // Insert new element at next(first()) + list_insert(l, q1, list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(NULL); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the end of the list + list_insert(l, q, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(i + 1, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(NULL); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the beginning of the list + list_insert(l, q, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(n - i, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(NULL); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(NULL); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(NULL); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(NULL); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the generic list.\n" + "Does not use any free-handler to handle deallocation of dynamic memory.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Uses code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/.gitignore b/OU1/datastructures-v2.2.2.2/src/queue/.gitignore new file mode 100644 index 0000000..9fd76b1 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/.gitignore @@ -0,0 +1,2 @@ +queue_example +queue_example_internal diff --git a/OU1/datastructures-v2.2.2.2/src/queue/Makefile b/OU1/datastructures-v2.2.2.2/src/queue/Makefile new file mode 100644 index 0000000..5795e44 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/Makefile @@ -0,0 +1,49 @@ +MWE = queue_mwe1 queue_mwe2 queue_mwe1i queue_mwe2i queue_example queue_example_internal + +SRC = queue.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +queue_example_internal: queue_example_internal.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_example: queue_example.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe1: queue_mwe1.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe2: queue_mwe2.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe1i: queue_mwe1i.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe2i: queue_mwe2i.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: queue_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: queue_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: queue_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: queue_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/queue/README.md b/OU1/datastructures-v2.2.2.2/src/queue/README.md new file mode 100644 index 0000000..f6746dd --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/README.md @@ -0,0 +1,11 @@ +# Kö +En implementation av ADT:n _Kö_ baserad på en dubbel-länkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + +# Minimal working example + +Se [queue_mwe1.c](queue_mwe1.c) och [queue_mwe2.c](queue_mwe2.c). diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue.c b/OU1/datastructures-v2.2.2.2/src/queue/queue.c new file mode 100644 index 0000000..3b73bc7 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue.c @@ -0,0 +1,401 @@ +#include +#include +#include + +#include +#include + +/* + * Implementation of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.01 2024-01-16: Fix include to be with brackets, not citation marks. + * v2.0 2024-03-14: Added queue_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +/* + * The queue is implemented using the list abstract datatype. Almost + * everything is done by the list. + */ + +struct queue { + list *elements; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func) +{ + // Allocate the queue head. + queue *q = calloc(1, sizeof(*q)); + // Create an empty list. + q->elements = list_empty(kill_func); + + return q; +} + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q) +{ + return list_is_empty(q->elements); +} + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v) +{ + list_insert(q->elements, v, list_end(q->elements)); + return q; +} + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q) +{ + list_remove(q->elements, list_first(q->elements)); + return q; +} + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q) +{ + return list_inspect(q->elements, list_first(q->elements)); +} + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q) +{ + list_kill(q->elements); + free(q); +} + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func) +{ + printf("{ "); + list_pos pos = list_first(q->elements); + while (pos != list_end(q->elements)) { + print_func(list_inspect(q->elements, pos)); + pos = list_next(q->elements, pos); + if (pos != list_end(q->elements)) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the queue head struct. + * @il: Indentation level. + * @q: Queue to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const queue *q) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"elements\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(q), PTR2ADDR(q->elements), PTR2ADDR(q)); +} + +// Print edges from the queue head to the elements list. +static void print_head_edge(int indent_level, const queue *q) +{ + print_edge(indent_level, q, q->elements, "e", "elements", NULL); +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * queue_print_internal() - Print the queues internal structure in dot format. + * @l: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph QUEUE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "q [label=\"%04lx\" xlabel=\"q\"]\n", PTR2ADDR(q)); + iprintf(il, "q -> m%04lx\n", PTR2ADDR(q)); + } + + // Output the payload nodes before asking the list to print them + // to render them in their own subgraph. + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + list_pos p = list_first(q->elements); + while (!list_pos_is_equal(q->elements, p, list_end(q->elements))) { + print_value_node(il, list_inspect(q->elements, p), print_func); + // Advance + p = list_next(q->elements, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_queue_%d { label=\"Queue\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, q); + + if (q->elements) { + list_print_internal(q->elements, print_func, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, q); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue_example.c b/OU1/datastructures-v2.2.2.2/src/queue/queue_example.c new file mode 100644 index 0000000..9782f9b --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue_example.c @@ -0,0 +1,68 @@ +#include +#include + +#include + +/* + * A small example that uses the generic queue to store integers. + * Based on int_queue_example.c from int_queue. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-23: First public version. + */ + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + // Create the queue. + queue *q = queue_empty(int_kill); + + // The queue should be empty. Is it? + if (queue_is_empty(q)) { + printf("The newly created queue is empty.\n"); + } else { + printf("The newly created queue is NOT empty.\n"); + } + + for (int i = 11; i <= 13; i++) { + printf("Add the value %d at the end of the queue.\n", i); + int *v = int_create(i); + // Put some values in the queue. + q = queue_enqueue(q, v); + } + + int *v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", *v); + + // Remove one element from the queue. + printf("Remove one element.\n"); + q = queue_dequeue(q); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", *v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue_example_internal.c b/OU1/datastructures-v2.2.2.2/src/queue/queue_example_internal.c new file mode 100644 index 0000000..93d77e4 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue_example_internal.c @@ -0,0 +1,124 @@ +#include +#include + +#include + +// Size of internal string buffer +#define BUFLEN 400 + +/* + * A small example that uses a generic queue to store integers. + * + * Generates two kinds of output: + * 1) The 'outside' view showing the stored values only. + * 2) The 'inside' view showing the internal organization. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-24: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-24" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print a message, then the queue and wait for the user to press return. +void print_and_wait(const queue *q, const char *msg) +{ + printf("\nThe output from queue_print() shows the values stored in the queue\n"); + printf("\n%s\n", msg); + queue_print(q, print_int); + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_and_wait(const queue *q, const char *desc) +{ + // Print starting marker line. + printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n"); + printf("\n\n1) Copy the lines between the cut marks\n" + "2) Paste into the left half of the browser window.\n" + "3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n"); + printf("\n--- CUT HERE ---\n\n"); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("\n--- END CUT HERE ---\n\n"); + + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +int main(void) +{ + // Message buffer + char buf[BUFLEN]; + + printf("%s, %s %s: Example use of a typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q = queue_empty(int_kill); + + // Call print functions to show the content of the queue + print_and_wait(q, "The queue is empty"); + print_internal_and_wait(q, "The queue is empty"); + + for (int i = 11; i <= 13; i++) { + int *v = int_create(i); + q = queue_enqueue(q, v); + snprintf(buf, BUFLEN, "The value %d was added to the queue", i); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + } + + int *w = queue_front(q); + printf("The value at the front of the queue is: %d.\n", *w); + + // Remove one element from the queue. + q = queue_dequeue(q); + snprintf(buf, BUFLEN, "The front element was removed from the queue"); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + + w = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", *w); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe1.c b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe1.c new file mode 100644 index 0000000..c807911 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe1.c @@ -0,0 +1,89 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer queue without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q=queue_empty(NULL); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q=queue_enqueue(q,v); + } + + printf("--QUEUE before dequeueing--\n"); + queue_print(q, print_int); + + // Get value on top of queue. + int *v=queue_front(q); + // Remote element from queue. + q=queue_dequeue(q); + // Return memory + int_kill(v); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q, print_int); + + // Now we must empty the queue and free each value ourselves. + while (!queue_is_empty(q)) { + // Get value from top of queue. + int *v=queue_front(q); + // Remove element from queue. + q=queue_dequeue(q); + // Free value + int_kill(v); + } + // Finally, destroy the bare queue. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe1i.c new file mode 100644 index 0000000..d8f0bed --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe1i.c @@ -0,0 +1,134 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c that shows how the internal + * structure of a queue can be visualized. In this version, the queue + * "borrows" the payload memory, i.e., the user of the queue is + * responsible for deallocating the payload memory. See queue_mwe2i.c + * for a version where the queue takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call queue_print_internal. +void print_internal_with_cut_lines(const queue *q, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer queue without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue *q = queue_empty(NULL); + + printf("Empty queue from the outside:\n"); + queue_print(q, print_int); + print_internal_with_cut_lines(q, "Empty queue showing the internal structure"); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q = queue_enqueue(q, v); + } + + printf("Queue from the outside after inserting 3 values:\n"); + queue_print(q, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Queue after inserting 3 values.\n" + "All data is stored internally using a List.\n" + "Red lines are used for the queue payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the queue, i.e., the payload\n" + "memory will NOT be deallocated by the queue.\n\n" + "See queue_mwe2i for a queue example\nthat owns the payload memory."; + print_internal_with_cut_lines(q, long_desc); + + // Now we must empty the queue and free each value ourselves. + while (!queue_is_empty(q)) { + // Get value from top of queue. + int *v = queue_front(q); + // Remove element from queue. + q = queue_dequeue(q); + // Free value + int_kill(v); + } + // Finally, destroy the bare queue. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe2.c b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe2.c new file mode 100644 index 0000000..ad27458 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe2.c @@ -0,0 +1,75 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer queue with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q = queue_empty(int_kill); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q=queue_enqueue(q,v); + } + + printf("--QUEUE before dequeueing--\n"); + queue_print(q, print_int); + + // Dequeue one element. The payload memory will be automatically returned. + queue_dequeue(q); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q, print_int); + + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe2i.c new file mode 100644 index 0000000..93d851a --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/queue/queue_mwe2i.c @@ -0,0 +1,126 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c that shows how the internal + * structure of a queue can be visualized. In this version, the queue + * "owns" the payload memory, i.e., the queue takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See queue_mwe1i.c for a version + * where the queue does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call queue_print_internal. +void print_internal_with_cut_lines(const queue *q, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer queue with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue *q = queue_empty(int_kill); + + printf("Empty queue from the outside:\n"); + queue_print(q, print_int); + print_internal_with_cut_lines(q, "Empty queue showing the internal structure"); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q = queue_enqueue(q, v); + } + + printf("Queue from the outside after inserting 3 values:\n"); + queue_print(q, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Queue after inserting 3 values.\n" + "All data is stored internally using a List.\n" + "Red lines are used for the queue payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the queue, i.e., the payload\n" + "memory WILL be deallocated by the queue.\n\n" + "See queue_mwe1i for a queue example\nthat borrows the payload memory."; + print_internal_with_cut_lines(q, long_desc); + + // Destroy the queue and its content. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/stack/Makefile b/OU1/datastructures-v2.2.2.2/src/stack/Makefile new file mode 100644 index 0000000..d2db60b --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/Makefile @@ -0,0 +1,43 @@ +MWE = stack_mwe1 stack_mwe2 stack_mwe1i stack_mwe2i + +SRC = stack.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +stack_mwe1: stack_mwe1.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe2: stack_mwe2.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe1i: stack_mwe1i.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe2i: stack_mwe2i.c stack.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: stack_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: stack_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: stack_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: stack_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU1/datastructures-v2.2.2.2/src/stack/README.md b/OU1/datastructures-v2.2.2.2/src/stack/README.md new file mode 100644 index 0000000..0322013 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/README.md @@ -0,0 +1,11 @@ +# Stack +En implementation av ADT:n _Stack_ implementerad som en enkellänkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + +# Minimal working example + +Se [stack_mwe1.c](stack_mwe1.c) och [stack_mwe2.c](stack_mwe2.c). diff --git a/OU1/datastructures-v2.2.2.2/src/stack/int_stack_test_marc.c b/OU1/datastructures-v2.2.2.2/src/stack/int_stack_test_marc.c new file mode 100644 index 0000000..c99cd2f --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/int_stack_test_marc.c @@ -0,0 +1,77 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ + +#include +/*#include */ + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the stack_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // create an empty stack + stack s = stack_empty(); + + + // l should be non-NULL + if (s == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(s); + fprintf(stderr,"done.\n"); +} + + + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/stack/stack.c b/OU1/datastructures-v2.2.2.2/src/stack/stack.c new file mode 100644 index 0000000..cd3a869 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/stack.c @@ -0,0 +1,463 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2022-03-24: Minor update to always take care of returned pointer. + * v2.0 2024-03-14: Added stack_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The stack elements are implemented as one-cells with forward and + * links. The stack has a single element pointer. + */ +typedef struct cell { + struct cell *next; + void *val; +} cell; + +struct stack { + cell *top; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * stack_empty() - Create an empty stack. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new stack. + */ +stack *stack_empty(kill_function kill_func) +{ + // Allocate memory for stack structure. + stack *s = calloc(1, sizeof(stack)); + s->top = NULL; + s->kill_func = kill_func; + + return s; +} + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack *s) +{ + return s->top == NULL; +} + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (pointer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_push(stack *s, void *v) +{ + // Allocate memory for element. + cell *e = calloc(1, sizeof(*e)); + // Set element value. + e->val = v; + // Link to current top. + e->next = s->top; + // Put element on top of stack. + s->top = e; + // Return modified stack. + return s; +} + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_pop(stack *s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_pop: Warning: pop on empty stack\n"); + } else { + // Remember top element. + cell *e = s->top; + // Link past top element. + s->top = s->top->next; + // De-allocate user memory. + if (s->kill_func != NULL) { + s->kill_func(e->val); + } + // De-allocate element memory. + free(e); + } + return s; +} + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +void *stack_top(const stack *s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_top: Warning: top on empty stack\n"); + } + return s->top->val; +} + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Return all dynamic memory used by the stack and its elements. If a + * kill_func was registered at stack creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void stack_kill(stack *s) +{ + while (!stack_is_empty(s)) { + s = stack_pop(s); + } + free(s); +} + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * @print_func: Function called for each element. + * + * Iterates over the stack and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void stack_print(const stack *s, inspect_callback print_func) +{ + printf("{ "); + cell *e = s->top; + while (e != NULL) { + print_func(e->val); + e = e->next; + if (e != NULL) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the stack head struct. + * @indent_level: Indentation level. + * @s: Stack to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const stack *s) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"kill\\n%04lx|top\\n%04lx\"]\n", + PTR2ADDR(s), PTR2ADDR(s->kill_func), PTR2ADDR(s->top)); +} + +// Print edges from the stack head to the head cell. +static void print_head_edge(int indent_level, const stack *s) +{ + print_edge(indent_level, s, s->top, "t", "top", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const cell *p) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"val\\n%04lx|next\\n%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the stack owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const stack *s, const cell *p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + + if (s->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * stack_print_internal() - Print the stacks internal structure in dot format. + * @l: Stack to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the stack and outputs dot code that shows the internal + * structure of the stack. The dot code can be visualized by Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph STACK_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "s [label=\"%04lx\" xlabel=\"s\"]\n", PTR2ADDR(s)); + iprintf(il, "s -> m%04lx\n", PTR2ADDR(s)); + } + + // Print the subgraph to surround the Stack content + iprintf(il, "subgraph cluster_stack_%d { label=\"Stack\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, s); + + // Output the element nodes + cell *p = s->top; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = s->top; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, s); + + // Output the edges from each element + p = s->top; + while (p != NULL) { + print_elem_edges(il, s, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe1.c b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe1.c new file mode 100644 index 0000000..eba1d03 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe1.c @@ -0,0 +1,89 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer stack without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. + stack *s = stack_empty(NULL); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("--STACK before popping--\n"); + stack_print(s, print_int); + + // Get value on top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + + printf("--STACK after popping--\n"); + stack_print(s, print_int); + + // Now we must empty the stack and free each value ourselves. + while (!stack_is_empty(s)) { + // Get value from top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + } + // Finally, destroy the bare stack. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe1i.c new file mode 100644 index 0000000..c1968a8 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe1i.c @@ -0,0 +1,133 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c that shows how the internal + * structure of a stack can be visualized. In this version, the stack + * "borrows" the payload memory, i.e., the user of the stack is + * responsible for deallocating the payload memory. See stack_mwe2i.c + * for a version where the stack takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call stack_print_internal. +void print_internal_with_cut_lines(const stack *s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer stack without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack *s = stack_empty(NULL); + + printf("Empty stack from the outside:\n"); + stack_print(s, print_int); + print_internal_with_cut_lines(s, "Empty stack showing the internal structure"); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("Stack from the outside after pushing 3 values:\n"); + stack_print(s, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Stack after pushing 3 values.\n" + "Red lines are used for the stack payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the stack, i.e., the payload\n" + "memory will NOT be deallocated by the stack.\n\n" + "See stack_mwe2i for a stack example\nthat owns the payload memory."; + print_internal_with_cut_lines(s, long_desc); + + // Now we must empty the stack and free each value ourselves. + while (!stack_is_empty(s)) { + // Get value from top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + } + // Finally, destroy the bare stack. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe2.c b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe2.c new file mode 100644 index 0000000..fe4f01c --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe2.c @@ -0,0 +1,78 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2022-03-24: Update to always take care of returned pointer. + * v1.3 2023-01-14: Added printouts at start/end of main. + * v1.4 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.4" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer stack with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. Make the stack responsible for + // deallocation pushed values. + stack *s = stack_empty(int_kill); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("--STACK before popping--\n"); + stack_print(s, print_int); + + // Pop'ing will automatically deallocate the payload + s = stack_pop(s); + + printf("--STACK after popping--\n"); + stack_print(s, print_int); + + // Killing will automatically deallocate the payload + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe2i.c new file mode 100644 index 0000000..b1d63bc --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/stack/stack_mwe2i.c @@ -0,0 +1,125 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c that shows how the internal + * structure of a stack can be visualized. In this version, the stack + * "owns" the payload memory, i.e., the stack takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See stack_mwe1i.c for a version + * where the stack does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call stack_print_internal. +void print_internal_with_cut_lines(const stack *s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer stack with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack *s = stack_empty(int_kill); + + printf("Empty stack from the outside:\n"); + stack_print(s, print_int); + print_internal_with_cut_lines(s, "Empty stack showing the internal structure"); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("Stack from the outside after pushing 3 values:\n"); + stack_print(s, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Stack after pushing 3 values.\n" + "Red lines are used for the stack payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the stack, i.e., the payload\n" + "memory WILL be deallocated by the stack.\n\n" + "See stack_mwe1i for a stack example\nthat borrows the payload memory."; + print_internal_with_cut_lines(s, long_desc); + + // Kill the stack. The payload memory is returned automatically. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/Makefile b/OU1/datastructures-v2.2.2.2/src/table/Makefile new file mode 100644 index 0000000..e46509a --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/Makefile @@ -0,0 +1,68 @@ +MWE = table_mwe1 table_mwe2 table_mwe3 table2_mwe1 table2_mwe2 table2_mwe3 table_mwe1i table_mwe2i table2_mwe1i table2_mwe2i + +SRC = table.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +table_mwe1: table_mwe1.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe2: table_mwe2.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe3: table_mwe3.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe1i: table_mwe1i.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe2i: table_mwe2i.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe1: table_mwe1.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe2: table_mwe2.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe3: table_mwe3.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe1i: table_mwe1i.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe2i: table_mwe2i.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +memtest11: table_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest12: table_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest13: table_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest21: table2_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest22: table2_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest23: table2_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + diff --git a/OU1/datastructures-v2.2.2.2/src/table/README.md b/OU1/datastructures-v2.2.2.2/src/table/README.md new file mode 100644 index 0000000..752c86f --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/README.md @@ -0,0 +1,125 @@ +# Tabellimplementation + +Det som skiljer tabellen från de övriga datatyperna är att vi här måste hantera +både nycklar och värden. Denna hantering fungerar analogt med de övriga, men vi +konkretiserar ändå med ett par exempel på hur tabellen kan användas. + +## Jämförelsefunktion + +För att vår tabell ska fungera behöver vi specificera hur nycklar ska jämföras. +Detta tillåter oss att t.ex. använda strängar eller heltal som nycklar utan att +den underliggande implementationen behöver ändras. Detta bygger på en +`compare_function`, som tar två pekare och returnerar `0` ifall de är lika, `0>` +om första värdet är större än det andra, samt `0<` om första värdet är mindre än +det andra. I fallet med heltal kan detta se ut såhär: + +```c +static int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} +``` + +Vi kan då skapa en tabell med `table_empty(compare_ints, free, free) som vi +sedan kan använda såhär: + +```c +table *t = table_empty(compare_ints, free, free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); + +int key_to_lookup = 5; +table_lookup(t, &key_to_lookup); +``` + +Om vår `compare_function` är felaktigt implementerad så kommer inte +`table_lookup` hitta värdet, trots att vi stoppat in det. + +## Minneshantering + +När vi skapar en tabell måste vi specificera hur minnet för nycklar och värden +ska hanteras. Ofta kommer vi vilja allokera minne på heapen dynamiskt, och låta +tabellen avallokera både nyklar och värden: + +```c +table *t = table_empty(compare_ints, free, free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); +table_kill(t); +``` + +Vi skulle även kunna låta nycklarna ligga på stacken, och bara allokera värden +på heapen enl. följande exempel: + +```c +table *t = table_empty(compare_ints, NULL, free); + +int key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, &key, value); +table_kill(t); +``` + +Det skulle också kunna vara så att nycklarna allokeras dynamiskt, men att de +också stoppas i en annan datatyp (t.ex. en lista). Följande kodsnutt illustrera +det exemplet: + +```c +table *t = table_empty(compare_ints, NULL, free); +list *l = list_empty(free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); +list_insert(l, key, list_first(l)); + +// Kommer inte frigöra key, bara value +table_kill(t); + +// Frigör också key +list_kill(l); +``` + +## Utskrift + +Till skillnad från de övriga datatyperna så vill vi skriva ut både nyckeln och +värdet med vår print-funktion. Istället för att exponera den interna `struct`:en +`table_entry` utanför c-filen, så använder vi oss istället av en print-funktion +som tar två parameterar. + +```c +static void print_int_string_pair(const void *key, const void *value) +{ + printf("[%d, %s]\n", *(int*)key, (char*)value); +} +``` + +Ett anrop till `table_print(t, print_int_string_pair)` skulle då skriva ut `[5, + test]` om vi utgår ifrån det tidigare exemplet. + + +# Minimal Working Example + +Se [table_mwe1.c](table_mwe1.c), [table_mwe2.c](table_mwe2.c) och [table_mwe3.c](table_mwe3.c). diff --git a/OU1/datastructures-v2.2.2.2/src/table/table.c b/OU1/datastructures-v2.2.2.2/src/table/table.c new file mode 100644 index 0000000..926e916 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table.c @@ -0,0 +1,687 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2019-03-04: Bugfix in table_remove. + * v1.2 2024-04-15: Added table_print_internal. + * v2.0 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(table_entry_kill); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + // Iterate over the list. Return first match. + + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + // If yes, return the corresponding value pointer. + return e->value; + } + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/table2.c b/OU1/datastructures-v2.2.2.2/src/table/table2.c new file mode 100644 index 0000000..c073ee5 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table2.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2019-02-21: Second version without dlist/memfreehandler. + * v1.2 2019-03-04: Bugfix in table_remove. + * v1.3 2024-04-15: Added table_print_internal. + * v2.0 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(NULL); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + // Iterate over the list. Return first match. + + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + // If yes, return the corresponding value pointer. + return e->value; + } + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/table_mwe1.c b/OU1/datastructures-v2.2.2.2/src/table/table_mwe1.c new file mode 100644 index 0000000..5b7dac5 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table_mwe1.c @@ -0,0 +1,135 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Makes two lookups and prints the + * result. The responsibility to deallocate the key-value pairs is NOT + * handed over to the table. Thus, all pointers must be stored outside + * the table. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.01 2019-03-05: Improved docs. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Keep track of the key-value pairs we allocate. + int *key[4]; + char *value[4]; + + // Keep the deallocation responsibility of the keys/values we allocate. + table *t = table_empty(compare_ints, NULL, NULL); + + key[0] = int_create(90187); + value[0] = string_copy("Umea"); + table_insert(t, key[0], value[0]); + + key[1] = int_create(90184); + value[1] = string_copy("Umea"); + table_insert(t, key[1], value[1]); + + key[2] = int_create(98185); + value[2] = string_copy("Kiruna"); + table_insert(t, key[2], value[2]); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + key[3] = int_create(90187); + value[3] = string_copy("Umea (Universitet)"); + table_insert(t, key[3], value[3]); + + printf("Table after adding a duplicate:\n"); + table_print(t, print_int_string_pair); + + v = 90187; + s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Kill the table, excluding the keys and values we entered. + table_kill(t); + + // Free key/value pairs that we put in the table. + for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) { + int_kill(key[i]); + string_kill(value[i]); + } + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/table_mwe1i.c b/OU1/datastructures-v2.2.2.2/src/table/table_mwe1i.c new file mode 100644 index 0000000..01e4bcb --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table_mwe1i.c @@ -0,0 +1,173 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Output dot/graphviz code to show + * the internal structure of the table. In this version, the table + * "borrows" the payload memory, i.e., the user of the table is + * responsible for deallocating the payload memory. See table_mwe2i.c + * for a version where the table takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-11: First public version. Adapted from table_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_string_int_pair(const void *key, const void *value) +{ + const char *k=key; + const int *v=value; + printf("[%s, %d]\n", k, *v); +} + +// Interpret the supplied key pointer and print its content. +void print_int(const void *key) +{ + const int *k=key; + printf("%d", *k); +} + +// Interpret the supplied value pointer and print its content. +void print_string(const void *value) +{ + const char *v=value; + printf("\\\"%s\\\"", v); +} + +// Compare two keys (char *). +int compare_strings(const void *k1, const void *k2) +{ + const char *key1 = k1; + const char *key2 = k2; + + return strcmp(key1, key2); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const table *t, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + table_print_internal(t, print_string, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Keep track of the key-value pairs we allocate. + char *key[3]; + int *value[3]; + + // Keep the deallocation responsibility of the keys/values we allocate. + table *t = table_empty(compare_strings, NULL, NULL); + + printf("Empty table from the outside:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Empty table showing the inside structure."); + + key[0] = string_copy("Jan"); + value[0] = int_create(31); + table_insert(t, key[0], value[0]); + + key[1] = string_copy("Feb"); + value[1] = int_create(28); + table_insert(t, key[1], value[1]); + + key[2] = string_copy("Mar"); + value[2] = int_create(31); + table_insert(t, key[2], value[2]); + + printf("Table from the outside after adding 3 key-value pairs:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Table with 3 key-value pairs showing the inside structure."); + + // Kill the table, excluding the keys and values we entered. + table_kill(t); + + // Free key/value pairs that we put in the table. + for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) { + string_kill(key[i]); + int_kill(value[i]); + } + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/table_mwe2.c b/OU1/datastructures-v2.2.2.2/src/table/table_mwe2.c new file mode 100644 index 0000000..6dd42eb --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table_mwe2.c @@ -0,0 +1,130 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Makes two lookups and prints the + * result. The responsibility to deallocate the key-value pairs IS + * handed over to the table. Thus, no key-value pointers need to be + * stored outside the table. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.01 2019-03-05: Improved docs. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table with kill_functions.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Delegate the deallocation responsibility to the table. + table *t = table_empty(compare_ints, int_kill, string_kill); + + // We don't need to store the keys and values outside the + // table since the deallocation will be done by the table. + int *key; + char *value; + + key = int_create(90187); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(90184); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(98185); + value = string_copy("Kiruna"); + table_insert(t, key, value); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s=table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + key = int_create(90187); + value = string_copy("Umea (Universitet)"); + table_insert(t, key, value); + + printf("Table after adding a duplicate:\n"); + table_print(t, print_int_string_pair); + + v = 90187; + s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Kill the table, including the keys and values we entered. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/table_mwe2i.c b/OU1/datastructures-v2.2.2.2/src/table/table_mwe2i.c new file mode 100644 index 0000000..1ceb2a0 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table_mwe2i.c @@ -0,0 +1,167 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Output dot/graphviz code to show + * the internal structure of the table. In this version, the table + * "owns" the payload memory, i.e., the table is responsible for + * deallocating the payload memory. See table_mwe1i.c for a version + * where the table borrows the memory. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-11: First public version. Adapted from table_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_string_int_pair(const void *key, const void *value) +{ + const char *k=key; + const int *v=value; + printf("[%s, %d]\n", k, *v); +} + +// Interpret the supplied key pointer and print its content. +void print_int(const void *key) +{ + const int *k=key; + printf("%d", *k); +} + +// Interpret the supplied value pointer and print its content. +void print_string(const void *value) +{ + const char *v=value; + printf("\\\"%s\\\"", v); +} + +// Compare two keys (char *). +int compare_strings(const void *k1, const void *k2) +{ + const char *key1 = k1; + const char *key2 = k2; + + return strcmp(key1, key2); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const table *t, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + table_print_internal(t, print_string, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Keep track of the key-value pairs we allocate. + char *key[3]; + int *value[3]; + + // Delegate the deallocation responsibility to the table. + table *t = table_empty(compare_strings, string_kill, int_kill); + + printf("Empty table from the outside:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, "Empty table showing the inside structure."); + + key[0] = string_copy("Jan"); + value[0] = int_create(31); + table_insert(t, key[0], value[0]); + + key[1] = string_copy("Feb"); + value[1] = int_create(28); + table_insert(t, key[1], value[1]); + + key[2] = string_copy("Mar"); + value[2] = int_create(31); + table_insert(t, key[2], value[2]); + + printf("Table from the outside after adding 3 key-value pairs:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Table with 3 key-value pairs showing the inside structure."); + + // Kill the table, including the keys and values we entered. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/table/table_mwe3.c b/OU1/datastructures-v2.2.2.2/src/table/table_mwe3.c new file mode 100644 index 0000000..f12d9c1 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/table/table_mwe3.c @@ -0,0 +1,129 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 3 key-value pairs into + * a table, no duplicates. Makes one lookup and prints the result. The + * responsibility to deallocate the key-value pairs is NOT handed over + * to the table. Uses table_choose_key() to extract keys and values to + * be able to destroy the table without memory leaks or externally + * stored pointers. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function " + "nor externally stored pointers, .\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Keep the deallocation responsibility. + table *t = table_empty(compare_ints, NULL, NULL); + + int *key; + char *value; + + key = int_create(90187); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(90184); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(98185); + value = string_copy("Kiruna"); + table_insert(t, key, value); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s=table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Simple cleanup that works if the table is without duplicates. + while (!table_is_empty(t)) { + // Get one key from the table. + key = table_choose_key(t); + // Lookup the corresponding value. + value = table_lookup(t, key); + // Remove the key-value pair from the table. + table_remove(t, key); + // De-allocate key and value. + int_kill(key); + string_kill(value); + } + // Kill what is left by the table. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU1/datastructures-v2.2.2.2/src/version/version.c b/OU1/datastructures-v2.2.2.2/src/version/version.c new file mode 100644 index 0000000..ac91937 --- /dev/null +++ b/OU1/datastructures-v2.2.2.2/src/version/version.c @@ -0,0 +1,26 @@ +/* + * File to hold library version information for the code base for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2023-01-14: First public version. + */ + +#include + +// Create a struct with all version information to be stored in the +// library file. +struct { + const char* long_version; + const char* version; + const unsigned major; + const unsigned minor; + const unsigned revision; + const unsigned patch; +} code_base_version = { + CODE_BASE_LONG_VERSION, CODE_BASE_VERSION, CODE_BASE_MAJOR_VERSION, + CODE_BASE_MINOR_VERSION, CODE_BASE_REVISION, CODE_BASE_PATCH +}; diff --git a/OU1/int_stack_test b/OU1/int_stack_test new file mode 100755 index 0000000..e98911a Binary files /dev/null and b/OU1/int_stack_test differ diff --git a/OU1/int_stack_test.c b/OU1/int_stack_test.c new file mode 100644 index 0000000..1e4bc1f --- /dev/null +++ b/OU1/int_stack_test.c @@ -0,0 +1,204 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ +#include + +/* + * test program for the stack implementation in stack.h. + * + * Author: Marc Meunier (mame0264@student.umu.se). + * + * Version information: + * 2025-04-22: v1.0. First version. + * 2025-05-01: v2.0. fixed comments from labres, see changes.txt for details. + */ + +#define VERSION "v2.0" +#define VERSION_DATE "2025-05-01" + + +// +// Function to compare the values stored in the list. +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + + +// test that the stack_empty() function creates an empty stack. +// no input parameters +// returns nothing +void isempty_return_true(void) +{ + fprintf(stderr, "Starting isempty_return_true()...\n"); + // initialize stack + stack s = stack_empty(); + + // checks false positive + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is empty but \"stack_is_empty\" reports non empty\n"); + exit(1); + } + + // adds values to stack + s = stack_push(s, 10); + + // test if stack prints false with values inside + if (stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but \"stack_is_empty\" reports empty.\n"); + + } + + fprintf(stderr, "done.\n"); +} + + +// tests if stack returns top value in stack. +// no input parameters +// returns nothing +void push_stack_on_top(void) +{ + fprintf(stderr, "Starting push_stack_on_top()...\n"); + + // initialize stack + stack s = stack_empty(); + + // adds values to stack + for (int i = 1; i <= 10; i++) + { + s = stack_push(s, i); + } + + int a = 0; + + // check if correct stack has correct order + for (int i = 0; i <= 9; i++) + { + a = 10 - i; + + // check if value is same as the top value in stack + if (!value_equal(stack_top(s), a)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", a, stack_top(s)); + exit(1); + } + + // removes one value from the stack + s = stack_pop(s); + } + + // Check if stack is empty after running test. + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but should be empty.\n"); + exit(1); + } + + fprintf(stderr, "done.\n"); +} + + +// tests if the top reads wrong value. +// no input parameters +// returns nothing +void top_read_wrong(void) +{ + fprintf(stderr, "Starting top_read_wrong()...\n"); + + // initialize stack + stack s = stack_empty(); + + // loops over values to add to the stack. + for (int i = 0; i <= 3; i++) + { + // adds value to stack + s = stack_push(s,i); + + // check if value is not same as inserted value and prinnt error message + if (!value_equal(stack_top(s), i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stack_top(s)); + exit(1); + } + } + + // removes values from the stack + for (int i = 0; i <= 3; i++) + { + s = stack_pop(s); + } + + // Check if stack is empty after running test. + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but should be empty.\n"); + exit(1); + } + + fprintf(stderr,"done.\n"); +} + + +// tests if the pop function removes the wrong value or not. +// no input parameters +// returns nothing +void pop_remove_wrong(void) +{ + fprintf(stderr, "Starting pop_remove_wrong()...\n"); + + // initialize stack + stack s = stack_empty(); + + // how many values to add to stack + int iter = 3; + + // adds values to stack + for (int i = 0; i <= iter; i++) + { + s = stack_push(s,i); + } + + // check if values are correct order + for (int i = 0; i <= iter; i++) + { + if (!value_equal(stack_top(s), 3-i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stack_top(s)); + exit(1); + } + + // removes one value from the stack + s = stack_pop(s); + } + + // Check if stack is empty after running test. + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but should be empty.\n"); + exit(1); + } + + fprintf(stderr,"done.\n"); +} + + + +// main function to run the tests- +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + + // runs tests + isempty_return_true(); + push_stack_on_top(); + top_read_wrong(); + pop_remove_wrong(); + + // test sucessful + fprintf(stderr, "\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/int_stack_test_inlämnad.c b/OU1/int_stack_test_inlämnad.c new file mode 100644 index 0000000..b2eb49f --- /dev/null +++ b/OU1/int_stack_test_inlämnad.c @@ -0,0 +1,155 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ + +#include +/*#include */ + +/* + * test program for the stack implementation in stack.h. + * + * Author: Marc Meunier (mame0264@student.umu.se). + * + * Version information: + * 2025-04-22: v1.0. First version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-04-22" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +// test that the stack_empty() function creates an empty stack. + +void isempty_return_true(void) +{ + // print start message + fprintf(stderr, "Starting isempty_return_true()...\n"); + // initialize stack + stack s = stack_empty(); + // prints if function returns false + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is empty but \"stack_is_empty\" reports non empty\n"); + } + + // adds values to stack + s = stack_push(s, 10); + // test if stack prints false with values inside + if (stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but \"stack_is_empty\" reports empty.\n"); + + } + // print success message + fprintf(stderr, "done.\n"); +} + +// tests if stack returns top value in stack. +void push_stack_on_top(void) +{ + // startmessage + fprintf(stderr, "Starting push_stack_on_top()...\n"); + // initialize stack + stack s = stack_empty(); + + + // continues test where adding values to stack in a loop + for (int i = 1; i <= 10; i++) + { + s = stack_push(s, i); + } + + int a = 0; + // tests if values are returned in correct order when more values are in stack. + for (int i = 0; i <= 9; i++) + { + a = 10 - i; + // check if value is same as the top value in stack and print message on error + if (!value_equal(stack_top(s), a)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", a, stack_top(s)); + } + // removes one value from the stack to continue loop + s = stack_pop(s); + } + // print function sucess message + fprintf(stderr, "done.\n"); +} +// tests if the top reads wrong value. +void top_read_wrong(void) +{ + // start message + fprintf(stderr, "Starting top_read_wrong()...\n"); + // initialize stack + stack s = stack_empty(); + // loops over values to add to the stack. + for (int i = 0; i <= 3; i++) + { + // adds value to stack + s = stack_push(s,i); + // check if value is not same as inserted value and prinnt error message + if (stack_top(s) != i) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stack_top(s)); + exit(1); + } + } + // function sucess message + fprintf(stderr,"done.\n"); +} +// test if pop function removes the wrong value or works. +void pop_remove_wrong(void) +{ + // startmessage + fprintf(stderr, "Starting pop_remove_wrong()...\n"); + // stack iterations + int iter = 3; + // initialize stack + stack s = stack_empty(); + // adds values to stack + for (int i = 0; i <= iter; i++) + { + s = stack_push(s,i); + } + // check if values are correct + for (int i = 0; i <= iter; i++) + { + if (!value_equal(stack_top(s), 3-i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stack_top(s)); + } + // removes top value from stack to check if it removes wrong later in loop. + s = stack_pop(s); + } + // end message + fprintf(stderr,"done.\n"); +} + + +// main function to run the tests- +int main(void) +{ + // prinit start message + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + // function stack_empty(); + isempty_return_true(); + // function stack_push(); + push_stack_on_top(); + // function stack_top(); + top_read_wrong(); + // function stack_pop(); + pop_remove_wrong(); + // if everything sucessful print sucess message. + fprintf(stderr, "\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + // exit without error. + return 0; +} diff --git a/OU1/int_stack_test_marc.c b/OU1/int_stack_test_marc.c new file mode 100644 index 0000000..82a3947 --- /dev/null +++ b/OU1/int_stack_test_marc.c @@ -0,0 +1,101 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ + +#include +/*#include */ + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + + + + + +void isempty_return_true(void) +{ + stack s = stack_empty(); + if (stack_is_empty(s) == false){ + fprintf(stderr,"\nStack is empty but \"stack_is_empty\" reports non empty\n"); + /*return 1;*/ + } + + + stack_print(s); + s = stack_push(s,10); + stack_print(s); + if (stack_is_empty(s) == true){ + fprintf(stderr,"\nStack is non empty but \"stack_is_empty\" reports empty.\n"); + /*return 1;*/ + } + + fprintf(stderr,"\n \"stack_is_empty\" function works!\n"); + stack_kill(s); + + {/* + s = stack_push(s,2); + int x; + x = stack_top(s); + fprintf(stderr,"value x: %x\n",x); + int truevalue; + truevalue = stack_is_empty(s); + + value_equal(truevalue,true); + + stack_print(s); + */ + /*if s == { }*/ + } + +} + +void push_stack_on_top(void) +{ + stack s = stack_empty(); + + s = stack_push(s,1); + s = stack_push(s,2); + + if (stack_top(s) == 1){ + fprintf(stderr,"\n\"stack_push\" puts values in wrong order.\n"); + } + +} + + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + + isempty_return_true(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/rapport_ou1.pdf b/OU1/rapport_ou1.pdf new file mode 100644 index 0000000..c08ee1d Binary files /dev/null and b/OU1/rapport_ou1.pdf differ diff --git a/OU1/stack_test b/OU1/stack_test new file mode 100755 index 0000000..0bbd134 Binary files /dev/null and b/OU1/stack_test differ diff --git a/OU1/stack_test.c b/OU1/stack_test.c new file mode 100644 index 0000000..0a4d78c --- /dev/null +++ b/OU1/stack_test.c @@ -0,0 +1,284 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ +#include + +/* + * test program for the stack implementation in stack.h. + * Uses different test functions to check if the stack implementation + * works as intended. + * + * Author: Marc Meunier (mame0264@student.umu.se). + * + * Version information: + * 2025-04-22: v1.0. First version. + * 2025-05-01: v2.0. fixed comments from labres, see changes.txt for details. + */ + +#define VERSION "v2.0" +#define VERSION_DATE "2025-05-01" + + +// +// Function to compare the values stored in the list. +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + + +// int_create() - make a dynamic copy of an integer +// @i: Integer to copy +// +// Returns: A pointer to a dynamic copy of i +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + + +// Return the memory used by the integer. +// +void int_kill(void *v) +{ + int *p = v; + free(p); +} + + +// test that the stack_empty() function creates an empty stack. +// no input parameters +// returns nothing +void test_stack_empty(void) +{ + + fprintf(stderr, "Starting test_stack_empty()...\n"); + + // initialize stack + stack *s = stack_empty(int_kill); + + // check that stack initialized correctly + if (s == NULL) + { + fprintf(stderr, "FAIL: stack_empty() returned NULL.\n"); + exit(1); + } + + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + + +// test that the stack_is_empty() function returns true for an empty stack +// no input parameters +// returns nothing +void isempty_return_true(void) +{ + fprintf(stderr, "Starting isempty_return_true()...\n"); + + // initialize stack + stack *s = stack_empty(int_kill); + + // prints if function returns false + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is empty but \"stack_is_empty\" reports non empty\n"); + exit(1); + } + + // adds values to stack + s = stack_push(s, int_create(10)); + + // test if stack prints false with values inside + + if (stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but \"stack_is_empty\" reports empty.\n"); + exit(1); + } + + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + + +// tests if stack returns top value in stack. +// no input parameters +// returns nothing +void push_stack_on_top(void) +{ + fprintf(stderr, "Starting push_stack_on_top()...\n"); + + // initialize stack + stack *s = stack_empty(int_kill); + + // how many numbers that should be added to stack + int numberofelements = 10; + + // adds nonlinear values to stack + for (int i = 1; i <= numberofelements; i++) + { + + if (i > 5) + { + int a = 5 * i; + s = stack_push(s, int_create((a))); + } + else + { + s = stack_push(s, int_create(i)); + } + } + + // top value of stack + int a = 0; + + // checks return order + for (int i = 0; i <= numberofelements - 1; i++) + { + a = numberofelements - i; + + // converts pointer to an intedger value. + int *stored_ptr = stack_top(s); + int stored_value = *stored_ptr; + + // checks if value higher then 5 so it has same values as inserted into stack. + if (a > 5) + { + if (!value_equal(stored_value, a * 5)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", (a * 5), stored_value); + exit(1); + } + } + else + { + if (!value_equal(stored_value, a)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", a, stored_value); + exit(1); + } + } + + // removes one value from the stack so that the next value can be checked. + s = stack_pop(s); + } + + // Check if stack is empty after running test. + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but should be empty.\n"); + exit(1); + } + + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + + +// tests if the top reads wrong value. +// no input parameters +// returns nothing +void top_read_wrong(void) +{ + fprintf(stderr, "Starting top_read_wrong()...\n"); + + // initialize stack + stack *s = stack_empty(int_kill); + + // loops over some values to add to the stack. + for (int i = 0; i <= 3; i++) + { + s = stack_push(s, int_create(i)); + + // converts the pointer to an intedger value. + int *stored_ptr = stack_top(s); + int stored_value = *stored_ptr; + + // checks values in stack and compare to inputed value. + if (!value_equal(stored_value, i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stored_value); + exit(1); + } + } + + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + + +// tests if the pop function removes the wrong value or not. +// no input parameters +// returns nothing +void pop_remove_wrong(void) +{ + fprintf(stderr, "Starting pop_remove_wrong()...\n"); + + // how many numbers that should be added to stack + int iter = 3; + + // initialize stack + stack *s = stack_empty(int_kill); + + // adds values to stack + for (int i = 0; i <= iter; i++) + { + s = stack_push(s, int_create(i)); + } + + // check if values are correct order in stack + for (int i = 0; i <= iter; i++) + { + int *stored_ptr = stack_top(s); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, 3 - i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stored_value); + exit(1); + } + + s = stack_pop(s); + } + + // Check if stack is empty after running test. + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but should be empty.\n"); + exit(1); + } + + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + + + +// main function that runs all the tests. +int main(void) +{ + // prints the version and date of the program + printf("%s, %s %s: Test program for the typed stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + + // tests each function. + test_stack_empty(); + isempty_return_true(); + push_stack_on_top(); + top_read_wrong(); + pop_remove_wrong(); + + // test sucessful + fprintf(stderr, "\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU1/stack_test_inlämnad.c b/OU1/stack_test_inlämnad.c new file mode 100644 index 0000000..15d7d40 --- /dev/null +++ b/OU1/stack_test_inlämnad.c @@ -0,0 +1,243 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ + +//#include +#include + +/* + * test program for the stack implementation in stack.h. + * + * Author: Marc Meunier (mame0264@student.umu.se). + * + * Version information: + * 2025-04-22: v1.0. First version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-04-22" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + + +// test that the stack_empty() function creates an empty stack. + +void test_stack_empty(void) +{ + // prints start message + fprintf(stderr, "Starting test_stack_empty()...\n"); + // initialize stack + stack *s = stack_empty(int_kill); + // check that stack initialized correctly + if (s == NULL) { + fprintf(stderr, "FAIL: stack_empty() returned NULL.\n"); + exit(1); + } + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + +// test that the stack_is_empty() function returns true for an empty stack +void isempty_return_true(void) +{ + // print start message + fprintf(stderr, "Starting isempty_return_true()...\n"); + // initialize stack + stack *s = stack_empty(int_kill); + // prints if function returns false + if (!stack_is_empty(s)) + { + fprintf(stderr, "\nStack is empty but \"stack_is_empty\" reports non empty\n"); + exit(1); + } + + // adds values to stack + stack_push(s, int_create(10)); + // test if stack prints false with values inside + if (stack_is_empty(s)) + { + fprintf(stderr, "\nStack is non empty but \"stack_is_empty\" reports empty.\n"); + exit(1); + } + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); +} + +// tests if stack returns top value in stack. +void push_stack_on_top(void) +{ + // startmessage + fprintf(stderr, "Starting push_stack_on_top()...\n"); + // initialize stack + stack *s = stack_empty(int_kill); + // how many numbers that should be added to stack + int numberofelements = 10; + // adds values to stack in + for (int i = 1; i <= numberofelements; i++) + { + // if the value i is higher then 5 then add in steps of 5 + // to check that the the stack_push does not just add value x in position x + if (i > 5) + { + int a = 5*i; + s = stack_push(s, int_create((a))); + + } + else + // if values are not higher just add next intedger + { + s = stack_push(s, int_create(i)); + } + } + // initialize a variable to store the value in the top of the stack + int a = 0; + // tests if values are returned in correct order when more values are in stack. + for (int i = 0; i <= numberofelements-1; i++) + { + a = numberofelements - i; + // check if value is same as the top value in stack. + // converts pointer to an intedger value. + int *stored_ptr = stack_top(s); + int stored_value = *stored_ptr; + + // checks if value higher then 5 so it has same values as inserted into stack. + if (a > 5) + { // if they are not equal print the error message + if (!value_equal(stored_value, a*5)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", (a*5), stored_value); + exit(1); + } + } + else + { // if they are not equal print the error message + if (!value_equal(stored_value, a)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", a, stored_value); + exit(1); + } + } + // removes one value from the stack so that the next value can be checked. + s = stack_pop(s); + } + // returns memory and exit message. + stack_kill(s); + fprintf(stderr, "done.\n"); + +} + +// tests if the top reads wrong value. +void top_read_wrong(void) +{ + // start message + fprintf(stderr, "Starting top_read_wrong()...\n"); + //initialize stack + stack *s = stack_empty(int_kill); + //loops over some values to add to the stack. + for (int i = 0; i <= 3; i++) + { + // adds the value to the stack + s = stack_push(s,int_create(i)); + // converts the pointer to an intedger value. + int *stored_ptr = stack_top(s); + int stored_value = *stored_ptr; + // checks values in stack and compare to inputed value. + if (!value_equal(stored_value, i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stored_value); + exit(1); + } + } + // returns memory and exit message. + stack_kill(s); + fprintf(stderr,"done.\n"); +} + +// tests if the pop function removes the wrong value or not. +void pop_remove_wrong(void) +{ + // startmessage + fprintf(stderr, "Starting pop_remove_wrong()...\n"); + // how many numbers that should be added to stack + int iter = 3; + // initialize stack + stack *s = stack_empty(int_kill); + // adds values to stack + for (int i = 0; i <= iter; i++) + { + s = stack_push(s,int_create(i)); + } + // check if values are correct and if removes them from the stack + for (int i = 0; i <= iter; i++) + { // converts the pointer to an intedger value. + int *stored_ptr = stack_top(s); + int stored_value = *stored_ptr; + // checks if the value is correct and if not print error message + if (!value_equal(stored_value, 3-i)) + { + fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stored_value); + exit(1); + } + // removes the value from the stack to continue loop over next value + s = stack_pop(s); + } + // returns memory and exit message. + stack_kill(s); + fprintf(stderr,"done.\n"); +} + + + +// main function that runs all the tests. +int main(void) +{ + // prints the version and date of the program + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + // tests each function. + // stack_empty(); + test_stack_empty(); + // stack_is_empty(); + isempty_return_true(); + // stack_push(); + push_stack_on_top(); + // stack_top(); + top_read_wrong(); + // stack_pop(); + pop_remove_wrong(); + + // if everything sucessfull then print sucessful message + fprintf(stderr, "\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + // returns 0 (program not crashed) + return 0; +} diff --git a/OU3/.gitignore b/OU3/.gitignore new file mode 100644 index 0000000..ddf1912 --- /dev/null +++ b/OU3/.gitignore @@ -0,0 +1,13 @@ +# Vim swap files. +*.swp +*.swo +# Object files. +*.o +# gcc precompiled headers. +*.gch +# Compiled programs for testing. +*_mwe +*_mwe[0-9] +*_mwe[0-9]i +*_test +*_test[0-9] diff --git a/OU3/.vscode/settings.json b/OU3/.vscode/settings.json new file mode 100644 index 0000000..2f792d4 --- /dev/null +++ b/OU3/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "format": "c", + "numbers": "c" + } +} \ No newline at end of file diff --git a/OU3/CODING_STYLE.md b/OU3/CODING_STYLE.md new file mode 100644 index 0000000..6bba949 --- /dev/null +++ b/OU3/CODING_STYLE.md @@ -0,0 +1,98 @@ +# Kodkonvention + +I detta dokument beskrivs den kodkonvention som har följts/ska följas +för kod som läggs till kodbasen. Konventionen är till stor del baserad +på kodkonventionen som följs i Linux-kärnan. Därför kommer läsaren +hänvisas till dokumentet med den officiella kernelkonventionen. + + +# Linuxkärnans kodkonvention + +Se [Linux kernel coding style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html). +Nedan följer en uppräkning av vilka punkter vi ska följa, samt +eventuella avvikelser från dessa. Om en punkt från dokumentet ovan ej +nämns nedan så gäller punkten ej. + +## 1. Indentation + +Indenteringskonventionen ska följas helt och hållet. Det är möjligt att +konfigurera utvecklingsmiljön (e.g. Codelite) att automatiskt indentera +korrekt. + +## 2. Breaking long lines and strings + +Denna punkt är också mycket viktig, samt något din utvecklingsmiljö kan +hantera eller varna för. + +## 3. Placing Braces and Spaces + +Här ska alla punkter följas (även de under 3.1), med ett undantag: +Ensamma satser i t.ex. if-satser *ska* ha måsvingar. + +## 4. Naming + +När det kommer till namngivning är det viktigt att ha i åtanke att koden +tillsammans med kommentarerna ska tydligt förklara syftet med koden. Därför är +det viktigt att konventionen som beskrivs under `4. Naming` tolkas med detta i +hänsyn. Det innebär till exempel att om koden blir enklare att läsa med +variabelnamnet `counter` istället för `c` eller `tmp`, bör `counter` användas +istället. + +## 5. Typedefs + +Här avviker kursen från Linuxkärnans kodkonvention. För att främja +datatypernas gränsytor och göra dem så lik de teoretiska gränsytorna +som möjligt så används `typedefs` för t.ex. `struct list_pos`. I +Linuxkärnan hade motsvarande implementation krävt att det skrevs +`struct list_pos pos` överallt, medan datatyperna givna på kursen +förenklar detta till `list_pos pos`. Det viktiga här är att lära sig +gränsytorna. + +## 6. Functions + +Denna konvention ska följas så som den presenteras. Långa funktioner kommer +under denna kurs alltid vara onödigt, dela upp i mindre funktioner! Det ökar +läsbarheten, förenklar debugging, samt gör det enklare att diskutera koden. + +## 7. Centralized exiting of functions + +Det finns anledningar till att `goto` används i Linuxkärnan (e.g. +bakåtkompabilitet), men under denna kurs ska ni ej använda detta. + +## 8. Commenting + +Följ det första exemplet för kommentarer. Det finns exempel i de givna +datatyperna på hur funktioner kan dokumenteras. + +## 14. Allocating memory + +I Linuxkärnan finns inte `malloc` och `calloc`, motsvarande funktioner heter +`kmalloc` samt `kcalloc`. Vi kommer under denna kurs att översätta Linuxkärnans +konvention till följande: + +```c +p = malloc(sizeof(*p)); +``` + +samt + +```c +p = calloc(n, sizeof(*p)); +``` + +Det är fullt möjligt att i det senare fallet vända på parametrarna (i.e. +`calloc(sizeof(*p), n)`) då storleken på minnet som allokeras beräknas som +första argumentet gånger det andra argumentet (i.e., +`n*sizeof(*p) == sizeof(*p)*n`). + +## 16. Function return values and names + +Här är det första och sista stycket vad som är viktigt att följa. Om en funktion +returnerar huruvida funktionen lyckades eller ej så signifierar `0` ett lyckat +anrop, och negativa värden signifierar fel (e.g. `-1` om en fil inte finns, `-2` +om filen inte gick att läsa). Om en funktion returnerar en pekare `p`, så +indikerar `p == NULL` att funktionen inte lyckades. + +# Avslutning + +Det finns visst rum för tolkning under några av dessa punkter. diff --git a/OU3/ChangeLog.txt b/OU3/ChangeLog.txt new file mode 100644 index 0000000..b5d3842 --- /dev/null +++ b/OU3/ChangeLog.txt @@ -0,0 +1,114 @@ +Summary changelog file for release. + +Release 2.2.2.2, Jan 24, 2025. +- Added int_queue_example_internal. +- Added symmetric examples to queue. + +Release 2.2.2.1, Jan 16, 2025. +- Added int_queue_example without any queue_print. + +Release 2.2.2.0, Jan 10, 2025. +- Added int_queue. +- Added release date to the code base. + +Release 2.2.1.5, May 15, 2024. +- Fixed array_1d merge confusion. + +Release 2.2.1.4, May 15, 2024. +- Whitespace/doc fix. + +Release 2.2.1.3, May 15, 2024. +- Bugfix to remove sprintf warning. + +Release 2.2.1.2, May 10, 2024. +- Untabified all code. + +Release 2.2.1.1, May 10, 2024. +- Bugfix to sprintf overflow warning. + +Release 2.2.1.0, May 10, 2024. +- Added typedef free_function for backward compatibility with pre-v2.0 code. + +Release 2.2.0.0, May 10, 2024. +- Added *_print_internal for all datatypes. +- Updated several *_print_internal functions to improve visualization of encapsulation. +- Added master Makefile to src. + +Release 2.1.0.0, Apr 15, 2024. +- Added *_print_internal for table, array_1d, array_2d and int_array_1d, int_stack. +- Updated comments in table.h to allow for different internal handling of duplicates. + +Release 2.0.0.1, Apr 05, 2024. +- Bugfix of one MWE in array_1d that never should have been changed. + +Release 2.0.0.0, Apr 04, 2024. +- Major upgrade with functions to print the internal memory structure + of the generic data types in graphviz/dot format. +- Added internal_print functions for list, dlist, queue, and stack. +- Added mweNi for list, dlist, queue, and stack. +- Renamed free_function to kill_function to reduce confusion with free() function. + +Release 1.0.14.3, Jan 16, 2024. +- Cleanup of some #include directives that used citation marks. + +Release 1.0.14.2, Mar 23, 2023. +- Cleanup of empty statements in some list test programs. + +Release 1.0.14.1, Mar 23, 2023. +- Renamed all list_pos_are_equal to list_pos_is_equal. + +Release 1.0.14.0, Mar 21, 2023. +- Cleaned up list_test programs. +- Renamed list header fields to head and tail. + +Release 1.0.13.0, Jan 21, 2023. +- Added two test programs to list and one to int_list_array. + +Release 1.0.12.0, Jan 21, 2023. +- Renamed list_pos_equal to list_pos_are_equal. +- Added int_list_test program. + +Release 1.0.11.0, Jan 19, 2023. +- Added list_pos_equal and list_pos_is_valid to list types. + +Release 1.0.10.0, Jan 14, 2023. +- Added version information to lib + all MWEs. + +Release 1.0.9, Mar 24, 2022. +- Added int_stack. + +Release 1.0.8, Mar 06, 2019. +- Added table_choose_key(). + +Release 1.0.7, Mar 04, 2019. +- Bugfix in table_remove(). +- Minor improvement of array_2d_print(). + +Release 1.0.6, Feb 21, 2019. +- Added table version without dlist/memhandler. +- Added Makefiles for each data type. +- Updated various README.md files. + +Release 1.0.5, Jan 23, 2019. +- Cleaned up list_test.c: No cleanup on failure, improved messages and + proper file comment. + +Release 1.0.4, Apr 03, 2018. +- Split MWE to one case per file. +- Modified list_test to NOT use memhandler. + +Release 1.0.3, Feb 08, 2018. +- Added table implementation. + +Release 1.0.2, Jan 29, 2018. +- Removed table from public area. +- Cleaned up links, tabbing of many files. +- Added list_test test file. +- Added ChangeLog (this file). +- Updated README.md + +Release 1.0.1, Jan 29, 2018. +- Removed array from public area. + +Release 1.0.0, Jan 28, 2018. +- First public release. diff --git a/OU3/README.md b/OU3/README.md new file mode 100644 index 0000000..cecfc92 --- /dev/null +++ b/OU3/README.md @@ -0,0 +1,114 @@ +# Datatypsimplementationer för kursen datastrukturer och algoritmer vid Institutionen för datavetenskap, Umeå universitet. + +# Version 2.2.2.2, 2025-01-24. + +# Givna datatyper + +Här återfinns implementationer av några av de datatyper som kommer användas +under denna kurs. Nedan följer information om hur dessa kompileras och hur +`minimal working examples` körs för varje datatyp. + +## Headerfiler + +Headerfilerna för de givna datatyperna är alla samlade i mappen `include`. Dessa +kan inkluderas med hjälp av `-I`-flaggan till `gcc`, se exempel nedan. + +## undefined reference + +Det är möjligt att du får t.ex. följande felmeddelande: + +```bash +/tmp/ccLd0e5H.o: In function `queue_empty': +queue.c:(.text+0x14): undefined reference to `list_empty' +``` + +Detta innebär att du inte anget alla nödvändiga C-filer till kompilatorn. I +fallet ovan saknas `list.c`. + +# En-dimensionellt fält + +```bash +user@host:~$ cd ~/datastructures/src/array_1d +user@host:~/datastructures/src/array_1d$ gcc -std=c99 -Wall -I../../include/ array_1d.c array_1d_mwe1.c -o array_1d_mwe1 +user@host:~/datastructures/src$ ./array_1d_mwe1 +[ [1], [4], [9], [16], [25], [36] ] +user@host:~/datastructures/src/array_1d$ gcc -std=c99 -Wall -I../../include/ array_1d.c array_1d_mwe2.c -o array_1d_mwe2 +user@host:~/datastructures/src$ ./array_1d_mwe2 +[ [(Jan, 31)], [(Feb, 28)], [(Mar, 31)], [(Apr, 30)], [(May, 31)], [(Jun, 30)], [(Jul, 31)], [(Aug, 31)], [(Sep, 30)], [(Oct, 31)], [(Nov, 30)], [(Dec, 31)] ] +``` +# Två-dimensionellt fält + +```bash +user@host:~$ cd ~/datastructures/src/array_2d +user@host:~/datastructures/src/array_2d$ gcc -std=c99 -Wall -I../../include/ array_2d.c array_2d_mwe1.c -o array_2d_mwe1 +user@host:~/datastructures/src/array_2d$ ./array_2d_mwe1 +[ [ [11], [12], [13] [ [21], [22], [23] [ [31], [32], [33] [ [41], [42], [43] ] +``` +# Lista (oriktad, dubbellänkad) + +```bash +user@host:~$ cd ~/datastructures/src/list +user@host:~/datastructures/src/list$ gcc -std=c99 -Wall -I../../include/ list.c list_mwe1.c -o list_mwe1 +user@host:~/datastructures/src/list$ ./list_mwe1 +List after inserting one value: +( [5] ) +List after inserting second value at the end: +( [5], [8] ) +List after inserting a third value in the middle: +( [5], [2], [8] ) +List after removing first element: +( [2], [8] ) +``` + +# Riktad lista (enkellänkad) + +```bash +user@host:~$ cd ~/datastructures/src/dlist +user@host:~/datastructures/src/dlist$ gcc -std=c99 -Wall -I../../include/ dlist.c dlist_mwe1.c -o dlist_mwe1 +user@host:~/datastructures/src/dlist$ ./dlist_mwe1 +("Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar") +``` + +# Kö + +```bash +user@host:~$ cd ~/datastructures/src/queue +user@host:~/datastructures/src/queue$ gcc -std=c99 -Wall -I../../include/ queue.c queue_mwe1.c ../list/list.c -o queue_mwe1 +user@host:~/datastructures/src/queue$ ./queue_mwe1 +QUEUE before dequeuing: +{ [1], [2], [3] } +QUEUE after dequeuing: +{ [2], [3] } +``` + + +# Stack + +```bash +user@host:~$ cd ~/datastructures/src/stack +user@host:~/datastructures/src/stack$ gcc -std=c99 -Wall -I../../include/ stack.c stack_mwe1.c -o stack_mwe1 +user@host:~/datastructures/src/stack$ ./stack_mwe1 +--STACK before popping-- +{ [3], [2], [1] } +--STACK after popping-- +{ [2], [1] } +``` + +# Tabell + +```bash +user@host:~$ cd ~/datastructures/src/table +user@host:~/datastructures/src/table$ gcc -std=c99 -Wall -I../../include/ table.c table_mwe1.c ../dlist/dlist.c -o table_mwe1 +user@host:~/datastructures/src/table$ ./table_mwe1 +Table after inserting 3 pairs: +[98185, Kiruna] +[90184, Umea] +[90187, Umea] +Lookup of postal code 90187: Umea. +Table after adding a duplicate: +[90187, Umea (Universitet)] +[98185, Kiruna] +[90184, Umea] +[90187, Umea] +Lookup of postal code 90187: Umea (Universitet). +``` diff --git a/OU3/arraytable (conflicted copy 2025-05-09 094442).c b/OU3/arraytable (conflicted copy 2025-05-09 094442).c new file mode 100644 index 0000000..39ac3e3 --- /dev/null +++ b/OU3/arraytable (conflicted copy 2025-05-09 094442).c @@ -0,0 +1,801 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +#define MAXSIZE 80000 + +/* + * Implementation of a arraybased table implementation + * + * Duplicates are handled by insert, where values are overwritten. + * + * Authors: Marc Meunier (tfy22mmr@cs.umu.se) + * + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table +{ + array_1d *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; + int count; +}; + +typedef struct table_entry +{ + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + // e->count = e->count + 1; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = array_1d_create(0, MAXSIZE - 1, NULL); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + t->count = 0; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return (t->count == 0); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + int inserted_values = 0; + // loop over array + for (int i = 0; i < MAXSIZE; i++) + { + // finds populated entries + if ((array_1d_inspect_value(t->entries, i) != NULL)) + { + inserted_values++; + table_entry *e; + e = array_1d_inspect_value(t->entries, i); + // checks if table key corresponds with seach key + if (e != NULL && t->key_cmp_func(e->key, key) == 0) + { + return e->value; + } + } + // will quit loop if no more values are left to check + if (inserted_values == t->count) + { + break; + } + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + // inserts value at first empty position if key is new + if (table_lookup(t, key) == NULL) + { + + for (int i = 0; i < MAXSIZE; i++) + { + // finds first empty slot + if (!(array_1d_inspect_value(t->entries, i) != NULL)) + { + array_1d_set_value(t->entries, e, i); + t->count++; + break; + } + } + } + // find place of old key and overwrites. + else + { + for (int i = 0; i < MAXSIZE; i++) + { + table_entry *f = array_1d_inspect_value(t->entries, i); + // find matching key + if (f != NULL && t->key_cmp_func(f->key, key) == 0) + { + // kills memory of old value and key + if (f != NULL) + { + if (t->value_kill_func != NULL) + { + t->value_kill_func(f->value); + } + if (t->key_kill_func != NULL) + { + t->key_kill_func(f->key); + } + table_entry_kill(f); + } + // insert new value + array_1d_set_value(t->entries, e, i); + break; + } + } + } +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Returns NULL for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // find first entrie in table + for (int i = 0; i < MAXSIZE - 1; i++) + { + // if contain entry + if ((array_1d_inspect_value(t->entries, i) != NULL)) + { + table_entry *e; + e = array_1d_inspect_value(t->entries, i); + return e->key; + } + } + return NULL; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + int inserted_values = 0; + for (int i = 0; i < MAXSIZE; i++) + { + if ((array_1d_inspect_value(t->entries, i) != NULL)) + { + inserted_values++; + table_entry *e = array_1d_inspect_value(t->entries, i); + // Find matching entry + if (e != NULL && t->key_cmp_func(e->key, key) == 0) + { + // removes memory from entries + if (e != NULL) + { + if (t->value_kill_func != NULL) + { + t->value_kill_func(e->value); + } + if (t->key_kill_func != NULL) + { + t->key_kill_func(e->key); + } + } + table_entry_kill(e); + // set values to NULL + array_1d_set_value(t->entries, NULL, i); + t->count--; + break; + } + } + // will quit loop if no more values are left to check + if (inserted_values == t->count) + { + break; + } + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + for (int i = 0; i < MAXSIZE; i++) + { + // Inspect the key/value pair. + table_entry *e = array_1d_inspect_value(t->entries, i); + // Kill key and/or value + if (e != NULL) + { + if (t->key_kill_func != NULL) + { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) + { + t->value_kill_func(e->value); + } + // Deallocate the table entry structure. + table_entry_kill(e); + } + } + + // Kill what's left of the list... + array_1d_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + for (int i = 0; i < MAXSIZE; i++) + { + table_entry *e = array_1d_inspect_value(t->entries, i); + // Call print_func + if (e != NULL) + { + print_func(e->key, e->value); + // pos = dlist_next(t->entries, i); + } + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i = 0; i < n; i++) + { + printf("\t"); + } +} +/** + * iprintf(...) - Indent and print. + * @n: Indentation level + * @...: printf arguments + * + * Print n tab characters and calls printf. + * + * Returns: Nothing. + */ +static void iprintf(int n, const char *fmt, ...) +{ + // Indent... + indent(n); + // ...and call printf + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +/** + * print_edge() - Print a edge between two addresses. + * @from: The address of the start of the edge. Should be non-NULL. + * @to: The address of the destination for the edge, including NULL. + * @port: The name of the port on the source node, or NULL. + * @label: The label for the edge, or NULL. + * @options: A string with other edge options, or NULL. + * + * Print an edge from port PORT on node FROM to TO with label + * LABEL. If to is NULL, the destination is the NULL node, otherwise a + * memory node. If the port is NULL, the edge starts at the node, not + * a specific port on it. If label is NULL, no label is used. The + * options string, if non-NULL, is printed before the label. + * + * Returns: Nothing. + */ +static void print_edge(int indent_level, const void *from, const void *to, const char *port, + const char *label, const char *options) +{ + indent(indent_level); + if (port) + { + printf("m%04lx:%s -> ", PTR2ADDR(from), port); + } + else + { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) + { + printf("NULL"); + } + else + { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) + { + printf("%s", options); + } + if (label != NULL) + { + printf(" label=\"%s\"", label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) + { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) + { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) + { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) + { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) + { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } + else + { + if (t->key_kill_func) + { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } + else + { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) + { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } + else + { + if (t->value_kill_func) + { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } + else + { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) + { + return; + } + for (int i = 0; i < MAXSIZE; i++) + { + table_entry *e = array_1d_inspect_value(t->entries, i); + if (e != NULL) + { + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) + { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') + { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) + { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) + { + case '\n': + t[i + j] = '\\'; + t[i + j + 1] = 'n'; + j++; + break; + case '\t': + t[i + j] = '\\'; + t[i + j + 1] = 't'; + j++; + break; + case '\\': + t[i + j] = '\\'; + t[i + j + 1] = '\\'; + j++; + break; + case '\"': + t[i + j] = '\\'; + t[i + j + 1] = '\"'; + j++; + break; + default: + t[i + j] = s[i]; + break; + } + } + // Terminal the output string + t[i + j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') + { + if (isspace(*t)) + { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) + { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } + else + { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) + { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) + { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il + 1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) + { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) + { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } + else + { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) + { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) + { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + for (int i = 0; i < MAXSIZE; i++) + { + print_key_value_nodes(il, array_1d_inspect_value(t->entries, i), key_print_func, + value_print_func); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) + { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/arraytable.c b/OU3/arraytable.c new file mode 100644 index 0000000..efae98d --- /dev/null +++ b/OU3/arraytable.c @@ -0,0 +1,801 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +#define MAXSIZE 80000 + +/* + * Implementation of a arraybased table implementation + * + * Duplicates are handled by insert, where values are overwritten. + * + * Authors: Marc Meunier (tfy22mmr@cs.umu.se) + * + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table +{ + array_1d *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; + int count; // total entries in table +}; + +typedef struct table_entry +{ + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + // e->count = e->count + 1; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = array_1d_create(0, MAXSIZE - 1, NULL); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + t->count = 0; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return (t->count == 0); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + int inserted_values = 0; + // loop over array + for (int i = 0; i < MAXSIZE; i++) + { + // finds populated entries + if ((array_1d_inspect_value(t->entries, i) != NULL)) + { + inserted_values++; + table_entry *e; + e = array_1d_inspect_value(t->entries, i); + // checks if table key corresponds with seach key + if (e != NULL && t->key_cmp_func(e->key, key) == 0) + { + return e->value; + } + } + // will quit loop if no more values are left to check + if (inserted_values == t->count) + { + break; + } + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + // inserts value at first empty position if key is new + if (table_lookup(t, key) == NULL) + { + + for (int i = 0; i < MAXSIZE; i++) + { + // finds first empty slot + if (!(array_1d_inspect_value(t->entries, i) != NULL)) + { + array_1d_set_value(t->entries, e, i); + t->count++; + break; + } + } + } + // find place of old key and overwrites. + else + { + for (int i = 0; i < MAXSIZE; i++) + { + table_entry *f = array_1d_inspect_value(t->entries, i); + // find matching key + if (f != NULL && t->key_cmp_func(f->key, key) == 0) + { + // kills memory of old value and key + if (f != NULL) + { + if (t->value_kill_func != NULL) + { + t->value_kill_func(f->value); + } + if (t->key_kill_func != NULL) + { + t->key_kill_func(f->key); + } + table_entry_kill(f); + } + // insert new value + array_1d_set_value(t->entries, e, i); + break; + } + } + } +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Returns NULL for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // find first entrie in table + for (int i = 0; i < MAXSIZE - 1; i++) + { + // if contain entry + if ((array_1d_inspect_value(t->entries, i) != NULL)) + { + table_entry *e; + e = array_1d_inspect_value(t->entries, i); + return e->key; + } + } + return NULL; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + int inserted_values = 0; + for (int i = 0; i < MAXSIZE; i++) + { + if ((array_1d_inspect_value(t->entries, i) != NULL)) + { + inserted_values++; + table_entry *e = array_1d_inspect_value(t->entries, i); + // Find matching entry + if (e != NULL && t->key_cmp_func(e->key, key) == 0) + { + // removes memory from entries + if (e != NULL) + { + if (t->value_kill_func != NULL) + { + t->value_kill_func(e->value); + } + if (t->key_kill_func != NULL) + { + t->key_kill_func(e->key); + } + } + table_entry_kill(e); + // set values to NULL + array_1d_set_value(t->entries, NULL, i); + t->count--; + break; + } + } + // will quit loop if no more values are left to check + if (inserted_values == t->count) + { + break; + } + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + for (int i = 0; i < MAXSIZE; i++) + { + // Inspect the key/value pair. + table_entry *e = array_1d_inspect_value(t->entries, i); + // Kill key and/or value + if (e != NULL) + { + if (t->key_kill_func != NULL) + { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) + { + t->value_kill_func(e->value); + } + // Deallocate the table entry structure. + table_entry_kill(e); + } + } + + // Kill what's left of the list... + array_1d_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + for (int i = 0; i < MAXSIZE; i++) + { + table_entry *e = array_1d_inspect_value(t->entries, i); + // Call print_func + if (e != NULL) + { + print_func(e->key, e->value); + // pos = dlist_next(t->entries, i); + } + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i = 0; i < n; i++) + { + printf("\t"); + } +} +/** + * iprintf(...) - Indent and print. + * @n: Indentation level + * @...: printf arguments + * + * Print n tab characters and calls printf. + * + * Returns: Nothing. + */ +static void iprintf(int n, const char *fmt, ...) +{ + // Indent... + indent(n); + // ...and call printf + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +/** + * print_edge() - Print a edge between two addresses. + * @from: The address of the start of the edge. Should be non-NULL. + * @to: The address of the destination for the edge, including NULL. + * @port: The name of the port on the source node, or NULL. + * @label: The label for the edge, or NULL. + * @options: A string with other edge options, or NULL. + * + * Print an edge from port PORT on node FROM to TO with label + * LABEL. If to is NULL, the destination is the NULL node, otherwise a + * memory node. If the port is NULL, the edge starts at the node, not + * a specific port on it. If label is NULL, no label is used. The + * options string, if non-NULL, is printed before the label. + * + * Returns: Nothing. + */ +static void print_edge(int indent_level, const void *from, const void *to, const char *port, + const char *label, const char *options) +{ + indent(indent_level); + if (port) + { + printf("m%04lx:%s -> ", PTR2ADDR(from), port); + } + else + { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) + { + printf("NULL"); + } + else + { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) + { + printf("%s", options); + } + if (label != NULL) + { + printf(" label=\"%s\"", label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) + { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) + { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) + { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) + { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) + { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } + else + { + if (t->key_kill_func) + { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } + else + { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) + { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } + else + { + if (t->value_kill_func) + { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } + else + { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) + { + return; + } + for (int i = 0; i < MAXSIZE; i++) + { + table_entry *e = array_1d_inspect_value(t->entries, i); + if (e != NULL) + { + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) + { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') + { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) + { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) + { + case '\n': + t[i + j] = '\\'; + t[i + j + 1] = 'n'; + j++; + break; + case '\t': + t[i + j] = '\\'; + t[i + j + 1] = 't'; + j++; + break; + case '\\': + t[i + j] = '\\'; + t[i + j + 1] = '\\'; + j++; + break; + case '\"': + t[i + j] = '\\'; + t[i + j + 1] = '\"'; + j++; + break; + default: + t[i + j] = s[i]; + break; + } + } + // Terminal the output string + t[i + j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') + { + if (isspace(*t)) + { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) + { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } + else + { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) + { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) + { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il + 1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) + { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) + { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } + else + { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) + { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) + { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + for (int i = 0; i < MAXSIZE; i++) + { + print_key_value_nodes(il, array_1d_inspect_value(t->entries, i), key_print_func, + value_print_func); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) + { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/arraytabletest b/OU3/arraytabletest new file mode 100755 index 0000000..f4347cd Binary files /dev/null and b/OU3/arraytabletest differ diff --git a/OU3/arraytabletest (conflicted copy 2025-05-08 202741) b/OU3/arraytabletest (conflicted copy 2025-05-08 202741) new file mode 100755 index 0000000..9cd664e Binary files /dev/null and b/OU3/arraytabletest (conflicted copy 2025-05-08 202741) differ diff --git a/OU3/atiming.txt b/OU3/atiming.txt new file mode 100644 index 0000000..d32b1cf --- /dev/null +++ b/OU3/atiming.txt @@ -0,0 +1,150 @@ +1, 10000, 621 +2, 10000, 322 +3, 10000, 933 +4, 10000, 464 +5, 10000, 464 +1, 10000, 619 +2, 10000, 334 +3, 10000, 934 +4, 10000, 472 +5, 10000, 471 +1, 10000, 617 +2, 10000, 340 +3, 10000, 946 +4, 10000, 476 +5, 10000, 475 +1, 10000, 620 +2, 10000, 339 +3, 10000, 948 +4, 10000, 475 +5, 10000, 470 +1, 10000, 618 +2, 10000, 335 +3, 10000, 916 +4, 10000, 474 +5, 10000, 462 +1, 10000, 608 +2, 10000, 332 +3, 10000, 935 +4, 10000, 458 +5, 10000, 476 +1, 13000, 1014 +2, 13000, 579 +3, 13000, 1542 +4, 13000, 759 +5, 13000, 771 +1, 13000, 1039 +2, 13000, 582 +3, 13000, 1547 +4, 13000, 751 +5, 13000, 777 +1, 13000, 1025 +2, 13000, 589 +3, 13000, 1553 +4, 13000, 769 +5, 13000, 784 +1, 13000, 1045 +2, 13000, 604 +3, 13000, 1578 +4, 13000, 760 +5, 13000, 766 +1, 13000, 1019 +2, 13000, 585 +3, 13000, 1533 +4, 13000, 756 +5, 13000, 765 +1, 13000, 1006 +2, 13000, 579 +3, 13000, 1525 +4, 13000, 778 +5, 13000, 791 +1, 16000, 1540 +2, 16000, 921 +3, 16000, 2319 +4, 16000, 1155 +5, 16000, 1159 +1, 16000, 1535 +2, 16000, 920 +3, 16000, 2320 +4, 16000, 1155 +5, 16000, 1156 +1, 16000, 1540 +2, 16000, 915 +3, 16000, 2311 +4, 16000, 1158 +5, 16000, 1157 +1, 16000, 1536 +2, 16000, 918 +3, 16000, 2344 +4, 16000, 1180 +5, 16000, 1171 +1, 16000, 1573 +2, 16000, 931 +3, 16000, 2349 +4, 16000, 1177 +5, 16000, 1184 +1, 16000, 1541 +2, 16000, 932 +3, 16000, 2348 +4, 16000, 1172 +5, 16000, 1145 +1, 19000, 2173 +2, 19000, 1469 +3, 19000, 3265 +4, 19000, 1628 +5, 19000, 1680 +1, 19000, 2192 +2, 19000, 1435 +3, 19000, 3269 +4, 19000, 1647 +5, 19000, 1638 +1, 19000, 2217 +2, 19000, 1460 +3, 19000, 3256 +4, 19000, 1621 +5, 19000, 1624 +1, 19000, 2162 +2, 19000, 1431 +3, 19000, 3310 +4, 19000, 1651 +5, 19000, 1658 +1, 19000, 2183 +2, 19000, 1433 +3, 19000, 3291 +4, 19000, 1614 +5, 19000, 1616 +1, 19000, 2147 +2, 19000, 1415 +3, 19000, 3301 +4, 19000, 1608 +5, 19000, 1620 +1, 22000, 2944 +2, 22000, 1847 +3, 22000, 4449 +4, 22000, 2233 +5, 22000, 2213 +1, 22000, 2940 +2, 22000, 1856 +3, 22000, 4426 +4, 22000, 2209 +5, 22000, 2201 +1, 22000, 2889 +2, 22000, 1803 +3, 22000, 4385 +4, 22000, 2215 +5, 22000, 2190 +1, 22000, 2876 +2, 22000, 1834 +3, 22000, 4430 +4, 22000, 2212 +5, 22000, 2221 +1, 22000, 2957 +2, 22000, 1814 +3, 22000, 4498 +4, 22000, 2174 +5, 22000, 2175 +1, 22000, 2860 +2, 22000, 1792 +3, 22000, 4283 +4, 22000, 2159 +5, 22000, 2130 diff --git a/OU3/changes (conflicted copy 2025-05-07 220750).txt b/OU3/changes (conflicted copy 2025-05-07 220750).txt new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/OU3/changes (conflicted copy 2025-05-07 220750).txt @@ -0,0 +1 @@ +# diff --git a/OU3/changes.txt b/OU3/changes.txt new file mode 100644 index 0000000..bac28b2 --- /dev/null +++ b/OU3/changes.txt @@ -0,0 +1,4 @@ +2025-05-16 + Lade till kommentar för tillägget i sturct table i arraytable implementationen. + + Fixat text i rapport som stack ut utanför sidan. \ No newline at end of file diff --git a/OU3/compile.sh b/OU3/compile.sh new file mode 100644 index 0000000..0187cd5 --- /dev/null +++ b/OU3/compile.sh @@ -0,0 +1,6 @@ +#!/bin/bash +gcc -Wall -I include/ -g -o tabletest tabletest.c src/table/table2.c src/dlist/dlist.c +gcc -Wall -I include/ -g -o mtftabletest tabletest.c mtftable.c src/dlist/dlist.c +gcc -Wall -I include/ -g -o arraytabletest tabletest.c arraytable.c src/array_1d/array_1d.c +#gcc -Wall -I include/ -g -o arraytabletest src/table/table_mwe1.c arraytable.c src/array_1d/array_1d.c + diff --git a/OU3/compile_print.sh b/OU3/compile_print.sh new file mode 100644 index 0000000..464183b --- /dev/null +++ b/OU3/compile_print.sh @@ -0,0 +1,2 @@ +#!/bin/bash +gcc -Wall -I include/ -g -o mtftableprint tableprint.c mtftable.c src/dlist/dlist.c diff --git a/OU3/fig1.eps b/OU3/fig1.eps new file mode 100644 index 0000000..6baf46f --- /dev/null +++ b/OU3/fig1.eps @@ -0,0 +1,1930 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: (MATLAB, The Mathworks, Inc. Version 24.2.0.2773142 \(R2024b\) Update 2. Operating System: Linux) +%%Title: ./fig1.eps +%%CreationDate: 2025-05-08T20:51:25 +%%Pages: (atend) +%%BoundingBox: 42 18 582 456 +%%LanguageLevel: 3 +%%EndComments +%%BeginProlog +%%BeginResource: procset (Apache XML Graphics Std ProcSet) 1.2 0 +%%Version: 1.2 0 +%%Copyright: (Copyright 2001-2003,2010 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/bd{bind def}bind def +/ld{load def}bd +/GR/grestore ld +/GS/gsave ld +/RM/rmoveto ld +/C/curveto ld +/t/show ld +/L/lineto ld +/ML/setmiterlimit ld +/CT/concat ld +/f/fill ld +/N/newpath ld +/S/stroke ld +/CC/setcmykcolor ld +/A/ashow ld +/cp/closepath ld +/RC/setrgbcolor ld +/LJ/setlinejoin ld +/GC/setgray ld +/LW/setlinewidth ld +/M/moveto ld +/re {4 2 roll M +1 index 0 rlineto +0 exch rlineto +neg 0 rlineto +cp } bd +/_ctm matrix def +/_tm matrix def +/BT { _ctm currentmatrix pop matrix _tm copy pop 0 0 moveto } bd +/ET { _ctm setmatrix } bd +/iTm { _ctm setmatrix _tm concat } bd +/Tm { _tm astore pop iTm 0 0 moveto } bd +/ux 0.0 def +/uy 0.0 def +/F { + /Tp exch def + /Tf exch def + Tf findfont Tp scalefont setfont + /cf Tf def /cs Tp def +} bd +/ULS {currentpoint /uy exch def /ux exch def} bd +/ULE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add moveto Tcx uy To add lineto + Tt setlinewidth stroke + grestore +} bd +/OLE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs add moveto Tcx uy To add cs add lineto + Tt setlinewidth stroke + grestore +} bd +/SOE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto + Tt setlinewidth stroke + grestore +} bd +/QT { +/Y22 exch store +/X22 exch store +/Y21 exch store +/X21 exch store +currentpoint +/Y21 load 2 mul add 3 div exch +/X21 load 2 mul add 3 div exch +/X21 load 2 mul /X22 load add 3 div +/Y21 load 2 mul /Y22 load add 3 div +/X22 load /Y22 load curveto +} bd +/SSPD { +dup length /d exch dict def +{ +/v exch def +/k exch def +currentpagedevice k known { +/cpdv currentpagedevice k get def +v cpdv ne { +/upd false def +/nullv v type /nulltype eq def +/nullcpdv cpdv type /nulltype eq def +nullv nullcpdv or +{ +/upd true def +} { +/sametype v type cpdv type eq def +sametype { +v type /arraytype eq { +/vlen v length def +/cpdvlen cpdv length def +vlen cpdvlen eq { +0 1 vlen 1 sub { +/i exch def +/obj v i get def +/cpdobj cpdv i get def +obj cpdobj ne { +/upd true def +exit +} if +} for +} { +/upd true def +} ifelse +} { +v type /dicttype eq { +v { +/dv exch def +/dk exch def +/cpddv cpdv dk get def +dv cpddv ne { +/upd true def +exit +} if +} forall +} { +/upd true def +} ifelse +} ifelse +} if +} ifelse +upd true eq { +d k v put +} if +} if +} if +} forall +d length 0 gt { +d setpagedevice +} if +} bd +/RE { % /NewFontName [NewEncodingArray] /FontName RE - + findfont dup length dict begin + { + 1 index /FID ne + {def} {pop pop} ifelse + } forall + /Encoding exch def + /FontName 1 index def + currentdict definefont pop + end +} bind def +%%EndResource +%%BeginResource: procset (Apache XML Graphics EPS ProcSet) 1.0 0 +%%Version: 1.0 0 +%%Copyright: (Copyright 2002-2003 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/BeginEPSF { %def +/b4_Inc_state save def % Save state for cleanup +/dict_count countdictstack def % Count objects on dict stack +/op_count count 1 sub def % Count objects on operand stack +userdict begin % Push userdict on dict stack +/showpage { } def % Redefine showpage, { } = null proc +0 setgray 0 setlinecap % Prepare graphics state +1 setlinewidth 0 setlinejoin +10 setmiterlimit [ ] 0 setdash newpath +/languagelevel where % If level not equal to 1 then +{pop languagelevel % set strokeadjust and +1 ne % overprint to their defaults. +{false setstrokeadjust false setoverprint +} if +} if +} bd +/EndEPSF { %def +count op_count sub {pop} repeat % Clean up stacks +countdictstack dict_count sub {end} repeat +b4_Inc_state restore +} bd +%%EndResource +%FOPBeginFontDict +%%IncludeResource: font Courier-Oblique +%%IncludeResource: font Courier-BoldOblique +%%IncludeResource: font Courier-Bold +%%IncludeResource: font ZapfDingbats +%%IncludeResource: font Symbol +%%IncludeResource: font Helvetica +%%IncludeResource: font Helvetica-Oblique +%%IncludeResource: font Helvetica-Bold +%%IncludeResource: font Helvetica-BoldOblique +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Italic +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-BoldItalic +%%IncludeResource: font Courier +%FOPEndFontDict +%%BeginResource: encoding WinAnsiEncoding +/WinAnsiEncoding [ +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /space /exclam /quotedbl +/numbersign /dollar /percent /ampersand /quotesingle +/parenleft /parenright /asterisk /plus /comma +/hyphen /period /slash /zero /one +/two /three /four /five /six +/seven /eight /nine /colon /semicolon +/less /equal /greater /question /at +/A /B /C /D /E +/F /G /H /I /J +/K /L /M /N /O +/P /Q /R /S /T +/U /V /W /X /Y +/Z /bracketleft /backslash /bracketright /asciicircum +/underscore /quoteleft /a /b /c +/d /e /f /g /h +/i /j /k /l /m +/n /o /p /q /r +/s /t /u /v /w +/x /y /z /braceleft /bar +/braceright /asciitilde /bullet /Euro /bullet +/quotesinglbase /florin /quotedblbase /ellipsis /dagger +/daggerdbl /circumflex /perthousand /Scaron /guilsinglleft +/OE /bullet /Zcaron /bullet /bullet +/quoteleft /quoteright /quotedblleft /quotedblright /bullet +/endash /emdash /asciitilde /trademark /scaron +/guilsinglright /oe /bullet /zcaron /Ydieresis +/space /exclamdown /cent /sterling /currency +/yen /brokenbar /section /dieresis /copyright +/ordfeminine /guillemotleft /logicalnot /sfthyphen /registered +/macron /degree /plusminus /twosuperior /threesuperior +/acute /mu /paragraph /middot /cedilla +/onesuperior /ordmasculine /guillemotright /onequarter /onehalf +/threequarters /questiondown /Agrave /Aacute /Acircumflex +/Atilde /Adieresis /Aring /AE /Ccedilla +/Egrave /Eacute /Ecircumflex /Edieresis /Igrave +/Iacute /Icircumflex /Idieresis /Eth /Ntilde +/Ograve /Oacute /Ocircumflex /Otilde /Odieresis +/multiply /Oslash /Ugrave /Uacute /Ucircumflex +/Udieresis /Yacute /Thorn /germandbls /agrave +/aacute /acircumflex /atilde /adieresis /aring +/ae /ccedilla /egrave /eacute /ecircumflex +/edieresis /igrave /iacute /icircumflex /idieresis +/eth /ntilde /ograve /oacute /ocircumflex +/otilde /odieresis /divide /oslash /ugrave +/uacute /ucircumflex /udieresis /yacute /thorn +/ydieresis +] def +%%EndResource +%FOPBeginFontReencode +/Courier-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Oblique exch definefont pop +/Courier-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-BoldOblique exch definefont pop +/Courier-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Bold exch definefont pop +/Helvetica findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica exch definefont pop +/Helvetica-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Oblique exch definefont pop +/Helvetica-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Bold exch definefont pop +/Helvetica-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-BoldOblique exch definefont pop +/Times-Roman findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Roman exch definefont pop +/Times-Italic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Italic exch definefont pop +/Times-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Bold exch definefont pop +/Times-BoldItalic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-BoldItalic exch definefont pop +/Courier findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier exch definefont pop +%FOPEndFontReencode +%%EndProlog +%%Page: 1 1 +%%PageBoundingBox: 0 0 637 477 +%%BeginPageSetup +N + 42 18 M + 624 18 L + 624 474 L + 42 474 L +cp +clip +[1 0 0 -1 0 477] CT +%%EndPageSetup +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +N +0 0 M +840 0 L +840 630 L +0 630 L +0 0 L +cp +clip +1 GC +N +0 0 840 630 re +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +0 0 840 630 re +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +109 262 M +760 262 L +760 47 L +109 47 L +cp +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 262 M +760 262 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 47 M +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 262 M +109 255.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +271.75 262 M +271.75 255.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +434.5 262 M +434.5 255.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +597.25 262 M +597.25 255.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 262 M +760 255.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 47 M +109 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +271.75 47 M +271.75 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +434.5 47 M +434.5 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +597.25 47 M +597.25 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 47 M +760 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 202.09473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-4 15 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 205.95788 202.09473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.3) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30524 202.09473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 452.6526 202.09473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.9) t +GR +GR +GS +[0.75789 0 0 0.75789 575.99996 202.09473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(2.2) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30547 217.49474] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-4.5 16 moveto +1 -1 scale +(n) t +GR +GR +GS +[0.75789 0 0 0.75789 550.23154 231.4421] CT +0.149 GC +N +1.906 -0.375 M +1.906 -0.469 1.969 -0.563 QT +4.672 -3.25 L +1.969 -5.953 L +1.906 -6.016 1.906 -6.125 QT +1.906 -6.219 1.984 -6.305 QT +2.063 -6.391 2.172 -6.391 QT +2.266 -6.391 2.375 -6.297 QT +5.063 -3.609 L +7.734 -6.297 L +7.844 -6.391 7.922 -6.391 QT +8.031 -6.391 8.109 -6.313 QT +8.188 -6.234 8.188 -6.125 QT +8.188 -6.016 8.125 -5.953 QT +5.422 -3.25 L +8.125 -0.563 L +8.188 -0.469 8.188 -0.375 QT +8.188 -0.266 8.109 -0.188 QT +8.031 -0.109 7.922 -0.109 QT +7.828 -0.109 7.734 -0.203 QT +5.063 -2.891 L +2.375 -0.203 L +2.266 -0.109 2.172 -0.109 QT +2.063 -0.109 1.984 -0.195 QT +1.906 -0.281 1.906 -0.375 QT +cp +f +GR +GS +[0.75789 0 0 0.75789 558.56838 231.4421] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 570.69469 226.89473] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 262 M +109 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 262 M +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 262 M +115.51 262 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 229.503 M +115.51 229.503 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 197.005 M +115.51 197.005 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 164.508 M +115.51 164.508 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 132.01 M +115.51 132.01 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 99.513 M +115.51 99.513 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 67.015 M +115.51 67.015 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 262 M +753.49 262 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 229.503 M +753.49 229.503 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 197.005 M +753.49 197.005 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 164.508 M +753.49 164.508 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 132.01 M +753.49 132.01 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 99.513 M +753.49 99.513 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 67.015 M +753.49 67.015 L +S +GR +GS +[0.75789 0 0 0.75789 78.61053 198.09473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(0) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 173.46511] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-27 5.5 moveto +1 -1 scale +(0.02) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 148.83548] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-27 5.5 moveto +1 -1 scale +(0.04) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 124.20586] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-27 5.5 moveto +1 -1 scale +(0.06) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 99.57623] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-27 5.5 moveto +1 -1 scale +(0.08) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 74.94659] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.1) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 50.31698] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-27 5.5 moveto +1 -1 scale +(0.12) t +GR +GR +GS +[0 -0.75789 0.75789 0 55.14738 116.62098] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-24 -5 moveto +1 -1 scale +(Time/n) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30556 33.06314] CT +/Helvetica-Bold 14.514 F +GS +[1 0 0 1 0 0] CT +-23 -5 moveto +1 -1 scale +(Insert) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +109 261.783 M +271.75 261.854 L +434.5 261.797 L +597.25 261.815 L +760 261.815 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 197.93054] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 205.95788 197.98422] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 329.30524 197.94081] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 452.6526 197.95429] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 575.99996 197.9548] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +109 261.783 M +271.75 261.771 L +434.5 261.797 L +597.25 261.786 L +760 261.815 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 197.93054] CT +0.851 0.325 0.098 RC +N +0 -3.628 M +2.004 -3.628 3.628 -2.004 3.628 0 C +3.628 0 L +3.628 2.004 2.004 3.628 0 3.628 C +-2.004 3.628 -3.628 2.004 -3.628 0 C +-3.628 -2.004 -2.004 -3.628 0 -3.628 C +cp +0 -4.288 M +-2.368 -4.288 -4.288 -2.368 -4.288 0 C +-4.288 2.368 -2.368 4.288 0 4.288 C +2.368 4.288 4.288 2.368 4.288 0 C +4.288 0 L +4.288 -2.368 2.368 -4.288 0 -4.288 C +cp +f +GR +GS +[0.75789 0 0 0.75789 205.95788 197.92105] CT +0.851 0.325 0.098 RC +N +/f-1825166512{0 -3.628 M +2.004 -3.628 3.628 -2.004 3.628 0 C +3.628 0 L +3.628 2.004 2.004 3.628 0 3.628 C +-2.004 3.628 -3.628 2.004 -3.628 0 C +-3.628 -2.004 -2.004 -3.628 0 -3.628 C +cp +0 -4.288 M +-2.368 -4.288 -4.288 -2.368 -4.288 0 C +-4.288 2.368 -2.368 4.288 0 4.288 C +2.368 4.288 4.288 2.368 4.288 0 C +4.288 0 L +4.288 -2.368 2.368 -4.288 0 -4.288 C +cp}def +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 329.30524 197.94081] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 452.6526 197.93269] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 575.99996 197.9548] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +109 161.718 M +271.75 133.927 L +434.5 105.183 L +597.25 75.653 L +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 122.09181] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 205.95788 101.02874] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 329.30524 79.24395] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 452.6526 56.86327] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 575.99996 35.14736] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +749 114 M +749 55 L +663 55 L +663 114 L +cp +f +GR +GS +[0.75789 0 0 0.75789 540.40532 50.0008] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 4.5 moveto +1 -1 scale +(T1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +668.994 66.598 M +708.539 66.598 L +S +GR +GS +[0.75789 0 0 0.75789 522.01264 50.0008] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 540.40532 63.56841] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 4.5 moveto +1 -1 scale +(M1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +668.994 84.5 M +708.539 84.5 L +S +GR +GS +[0.75789 0 0 0.75789 522.01264 63.56841] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 540.40532 77.13603] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 4.5 moveto +1 -1 scale +(A1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +668.994 102.402 M +708.539 102.402 L +S +GR +GS +[0.75789 0 0 0.75789 522.01264 77.13603] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +10.0 ML +0.66 LW +N +663 114 M +663 55 L +749 55 L +749 114 L +cp +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +109 561 M +760 561 L +760 346 L +109 346 L +cp +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +760 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 346 M +760 346 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +109 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +271.75 561 M +271.75 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +434.5 561 M +434.5 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +597.25 561 M +597.25 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +760 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 346 M +109 352.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +271.75 346 M +271.75 352.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +434.5 346 M +434.5 352.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +597.25 346 M +597.25 352.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 346 M +760 352.51 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-4 15 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 205.95788 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.3) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30524 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 452.6526 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.9) t +GR +GR +GS +[0.75789 0 0 0.75789 575.99996 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(2.2) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30547 444.10527] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-4.5 16 moveto +1 -1 scale +(n) t +GR +GR +GS +[0.75789 0 0 0.75789 550.23154 458.05263] CT +0.149 GC +N +/f-838442823{1.906 -0.375 M +1.906 -0.469 1.969 -0.563 QT +4.672 -3.25 L +1.969 -5.953 L +1.906 -6.016 1.906 -6.125 QT +1.906 -6.219 1.984 -6.305 QT +2.063 -6.391 2.172 -6.391 QT +2.266 -6.391 2.375 -6.297 QT +5.063 -3.609 L +7.734 -6.297 L +7.844 -6.391 7.922 -6.391 QT +8.031 -6.391 8.109 -6.313 QT +8.188 -6.234 8.188 -6.125 QT +8.188 -6.016 8.125 -5.953 QT +5.422 -3.25 L +8.125 -0.563 L +8.188 -0.469 8.188 -0.375 QT +8.188 -0.266 8.109 -0.188 QT +8.031 -0.109 7.922 -0.109 QT +7.828 -0.109 7.734 -0.203 QT +5.063 -2.891 L +2.375 -0.203 L +2.266 -0.109 2.172 -0.109 QT +2.063 -0.109 1.984 -0.195 QT +1.906 -0.281 1.906 -0.375 QT +cp}def +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 558.56838 458.05263] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 570.69469 453.50526] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +109 346 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +760 346 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +115.51 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 484.773 M +115.51 484.773 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 408.545 M +115.51 408.545 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +753.49 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 484.773 M +753.49 484.773 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 408.545 M +753.49 408.545 L +S +GR +GS +[0.75789 0 0 0.75789 78.61053 424.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(0) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 366.93302] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.5) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61053 309.16077] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(1) t +GR +GR +GS +[0 -0.75789 0.75789 0 61.21051 343.23151] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-24 -5 moveto +1 -1 scale +(Time/n) t +GR +GR +GS +[0.75789 0 0 0.75789 82.61052 258.72631] CT +0.149 GC +N +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 90.94736 258.72631] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 103.07368 254.17894] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(-4) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30556 259.67368] CT +/Helvetica-Bold 14.514 F +GS +[1 0 0 1 0 0] CT +-23 -5 moveto +1 -1 scale +(Insert) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +109 357.727 M +271.75 424.182 L +434.5 370.432 L +597.25 387.148 L +760 387.756 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 270.64592] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 205.95788 321.01148] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 329.30524 280.27464] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 452.6526 292.94398] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 575.99996 293.40469] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +109 357.727 M +271.75 346 L +434.5 370.432 L +597.25 360.402 L +760 387.756 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 270.64592] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 205.95788 261.75789] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 329.30524 280.27464] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 452.6526 272.67303] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 575.99996 293.40469] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +749 395 M +749 354 L +663 354 L +663 395 L +cp +f +GR +GS +[0.75789 0 0 0.75789 540.40532 276.59031] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 4.5 moveto +1 -1 scale +(T1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +668.994 365.571 M +708.539 365.571 L +S +GR +GS +[0.75789 0 0 0.75789 522.01264 276.59031] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 540.40532 290.12547] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 4.5 moveto +1 -1 scale +(M1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +668.994 383.429 M +708.539 383.429 L +S +GR +GS +[0.75789 0 0 0.75789 522.01264 290.12547] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +10.0 ML +0.66 LW +N +663 395 M +663 354 L +749 354 L +749 395 L +cp +S +GR +%%Trailer +%%Pages: 1 +%%EOF diff --git a/OU3/fig1.pdf b/OU3/fig1.pdf new file mode 100644 index 0000000..6e3b092 Binary files /dev/null and b/OU3/fig1.pdf differ diff --git a/OU3/fig2.eps b/OU3/fig2.eps new file mode 100644 index 0000000..939d883 --- /dev/null +++ b/OU3/fig2.eps @@ -0,0 +1,1343 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: (MATLAB, The Mathworks, Inc. Version 24.2.0.2773142 \(R2024b\) Update 2. Operating System: Linux) +%%Title: ./fig2.eps +%%CreationDate: 2025-05-08T20:51:26 +%%Pages: (atend) +%%BoundingBox: 57 18 582 456 +%%LanguageLevel: 3 +%%EndComments +%%BeginProlog +%%BeginResource: procset (Apache XML Graphics Std ProcSet) 1.2 0 +%%Version: 1.2 0 +%%Copyright: (Copyright 2001-2003,2010 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/bd{bind def}bind def +/ld{load def}bd +/GR/grestore ld +/GS/gsave ld +/RM/rmoveto ld +/C/curveto ld +/t/show ld +/L/lineto ld +/ML/setmiterlimit ld +/CT/concat ld +/f/fill ld +/N/newpath ld +/S/stroke ld +/CC/setcmykcolor ld +/A/ashow ld +/cp/closepath ld +/RC/setrgbcolor ld +/LJ/setlinejoin ld +/GC/setgray ld +/LW/setlinewidth ld +/M/moveto ld +/re {4 2 roll M +1 index 0 rlineto +0 exch rlineto +neg 0 rlineto +cp } bd +/_ctm matrix def +/_tm matrix def +/BT { _ctm currentmatrix pop matrix _tm copy pop 0 0 moveto } bd +/ET { _ctm setmatrix } bd +/iTm { _ctm setmatrix _tm concat } bd +/Tm { _tm astore pop iTm 0 0 moveto } bd +/ux 0.0 def +/uy 0.0 def +/F { + /Tp exch def + /Tf exch def + Tf findfont Tp scalefont setfont + /cf Tf def /cs Tp def +} bd +/ULS {currentpoint /uy exch def /ux exch def} bd +/ULE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add moveto Tcx uy To add lineto + Tt setlinewidth stroke + grestore +} bd +/OLE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs add moveto Tcx uy To add cs add lineto + Tt setlinewidth stroke + grestore +} bd +/SOE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto + Tt setlinewidth stroke + grestore +} bd +/QT { +/Y22 exch store +/X22 exch store +/Y21 exch store +/X21 exch store +currentpoint +/Y21 load 2 mul add 3 div exch +/X21 load 2 mul add 3 div exch +/X21 load 2 mul /X22 load add 3 div +/Y21 load 2 mul /Y22 load add 3 div +/X22 load /Y22 load curveto +} bd +/SSPD { +dup length /d exch dict def +{ +/v exch def +/k exch def +currentpagedevice k known { +/cpdv currentpagedevice k get def +v cpdv ne { +/upd false def +/nullv v type /nulltype eq def +/nullcpdv cpdv type /nulltype eq def +nullv nullcpdv or +{ +/upd true def +} { +/sametype v type cpdv type eq def +sametype { +v type /arraytype eq { +/vlen v length def +/cpdvlen cpdv length def +vlen cpdvlen eq { +0 1 vlen 1 sub { +/i exch def +/obj v i get def +/cpdobj cpdv i get def +obj cpdobj ne { +/upd true def +exit +} if +} for +} { +/upd true def +} ifelse +} { +v type /dicttype eq { +v { +/dv exch def +/dk exch def +/cpddv cpdv dk get def +dv cpddv ne { +/upd true def +exit +} if +} forall +} { +/upd true def +} ifelse +} ifelse +} if +} ifelse +upd true eq { +d k v put +} if +} if +} if +} forall +d length 0 gt { +d setpagedevice +} if +} bd +/RE { % /NewFontName [NewEncodingArray] /FontName RE - + findfont dup length dict begin + { + 1 index /FID ne + {def} {pop pop} ifelse + } forall + /Encoding exch def + /FontName 1 index def + currentdict definefont pop + end +} bind def +%%EndResource +%%BeginResource: procset (Apache XML Graphics EPS ProcSet) 1.0 0 +%%Version: 1.0 0 +%%Copyright: (Copyright 2002-2003 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/BeginEPSF { %def +/b4_Inc_state save def % Save state for cleanup +/dict_count countdictstack def % Count objects on dict stack +/op_count count 1 sub def % Count objects on operand stack +userdict begin % Push userdict on dict stack +/showpage { } def % Redefine showpage, { } = null proc +0 setgray 0 setlinecap % Prepare graphics state +1 setlinewidth 0 setlinejoin +10 setmiterlimit [ ] 0 setdash newpath +/languagelevel where % If level not equal to 1 then +{pop languagelevel % set strokeadjust and +1 ne % overprint to their defaults. +{false setstrokeadjust false setoverprint +} if +} if +} bd +/EndEPSF { %def +count op_count sub {pop} repeat % Clean up stacks +countdictstack dict_count sub {end} repeat +b4_Inc_state restore +} bd +%%EndResource +%FOPBeginFontDict +%%IncludeResource: font Courier-Oblique +%%IncludeResource: font Courier-BoldOblique +%%IncludeResource: font Courier-Bold +%%IncludeResource: font ZapfDingbats +%%IncludeResource: font Symbol +%%IncludeResource: font Helvetica +%%IncludeResource: font Helvetica-Oblique +%%IncludeResource: font Helvetica-Bold +%%IncludeResource: font Helvetica-BoldOblique +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Italic +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-BoldItalic +%%IncludeResource: font Courier +%FOPEndFontDict +%%BeginResource: encoding WinAnsiEncoding +/WinAnsiEncoding [ +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /space /exclam /quotedbl +/numbersign /dollar /percent /ampersand /quotesingle +/parenleft /parenright /asterisk /plus /comma +/hyphen /period /slash /zero /one +/two /three /four /five /six +/seven /eight /nine /colon /semicolon +/less /equal /greater /question /at +/A /B /C /D /E +/F /G /H /I /J +/K /L /M /N /O +/P /Q /R /S /T +/U /V /W /X /Y +/Z /bracketleft /backslash /bracketright /asciicircum +/underscore /quoteleft /a /b /c +/d /e /f /g /h +/i /j /k /l /m +/n /o /p /q /r +/s /t /u /v /w +/x /y /z /braceleft /bar +/braceright /asciitilde /bullet /Euro /bullet +/quotesinglbase /florin /quotedblbase /ellipsis /dagger +/daggerdbl /circumflex /perthousand /Scaron /guilsinglleft +/OE /bullet /Zcaron /bullet /bullet +/quoteleft /quoteright /quotedblleft /quotedblright /bullet +/endash /emdash /asciitilde /trademark /scaron +/guilsinglright /oe /bullet /zcaron /Ydieresis +/space /exclamdown /cent /sterling /currency +/yen /brokenbar /section /dieresis /copyright +/ordfeminine /guillemotleft /logicalnot /sfthyphen /registered +/macron /degree /plusminus /twosuperior /threesuperior +/acute /mu /paragraph /middot /cedilla +/onesuperior /ordmasculine /guillemotright /onequarter /onehalf +/threequarters /questiondown /Agrave /Aacute /Acircumflex +/Atilde /Adieresis /Aring /AE /Ccedilla +/Egrave /Eacute /Ecircumflex /Edieresis /Igrave +/Iacute /Icircumflex /Idieresis /Eth /Ntilde +/Ograve /Oacute /Ocircumflex /Otilde /Odieresis +/multiply /Oslash /Ugrave /Uacute /Ucircumflex +/Udieresis /Yacute /Thorn /germandbls /agrave +/aacute /acircumflex /atilde /adieresis /aring +/ae /ccedilla /egrave /eacute /ecircumflex +/edieresis /igrave /iacute /icircumflex /idieresis +/eth /ntilde /ograve /oacute /ocircumflex +/otilde /odieresis /divide /oslash /ugrave +/uacute /ucircumflex /udieresis /yacute /thorn +/ydieresis +] def +%%EndResource +%FOPBeginFontReencode +/Courier-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Oblique exch definefont pop +/Courier-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-BoldOblique exch definefont pop +/Courier-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Bold exch definefont pop +/Helvetica findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica exch definefont pop +/Helvetica-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Oblique exch definefont pop +/Helvetica-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Bold exch definefont pop +/Helvetica-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-BoldOblique exch definefont pop +/Times-Roman findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Roman exch definefont pop +/Times-Italic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Italic exch definefont pop +/Times-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Bold exch definefont pop +/Times-BoldItalic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-BoldItalic exch definefont pop +/Courier findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier exch definefont pop +%FOPEndFontReencode +%%EndProlog +%%Page: 1 1 +%%PageBoundingBox: 0 0 637 477 +%%BeginPageSetup +N + 57 18 M + 639 18 L + 639 474 L + 57 474 L +cp +clip +[1 0 0 -1 0 477] CT +%%EndPageSetup +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +N +0 0 M +840 0 L +840 630 L +0 630 L +0 0 L +cp +clip +1 GC +N +0 0 840 630 re +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +0 0 840 630 re +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +109 561 M +760 561 L +760 47 L +109 47 L +cp +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +760 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 47 M +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +109 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +271.75 561 M +271.75 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +434.5 561 M +434.5 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +597.25 561 M +597.25 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +760 554.49 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 47 M +109 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +271.75 47 M +271.75 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +434.5 47 M +434.5 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +597.25 47 M +597.25 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 47 M +760 53.51 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-4 15 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 205.95788 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.3) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30524 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 452.6526 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.9) t +GR +GR +GS +[0.75789 0 0 0.75789 575.99996 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(2.2) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30547 444.10527] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-4.5 16 moveto +1 -1 scale +(n) t +GR +GR +GS +[0.75789 0 0 0.75789 550.23154 458.05263] CT +0.149 GC +N +1.906 -0.375 M +1.906 -0.469 1.969 -0.563 QT +4.672 -3.25 L +1.969 -5.953 L +1.906 -6.016 1.906 -6.125 QT +1.906 -6.219 1.984 -6.305 QT +2.063 -6.391 2.172 -6.391 QT +2.266 -6.391 2.375 -6.297 QT +5.063 -3.609 L +7.734 -6.297 L +7.844 -6.391 7.922 -6.391 QT +8.031 -6.391 8.109 -6.313 QT +8.188 -6.234 8.188 -6.125 QT +8.188 -6.016 8.125 -5.953 QT +5.422 -3.25 L +8.125 -0.563 L +8.188 -0.469 8.188 -0.375 QT +8.188 -0.266 8.109 -0.188 QT +8.031 -0.109 7.922 -0.109 QT +7.828 -0.109 7.734 -0.203 QT +5.063 -2.891 L +2.375 -0.203 L +2.266 -0.109 2.172 -0.109 QT +2.063 -0.109 1.984 -0.195 QT +1.906 -0.281 1.906 -0.375 QT +cp +f +GR +GS +[0.75789 0 0 0.75789 558.56838 458.05263] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 570.69469 453.50526] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +109 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 561 M +115.51 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 470.393 M +115.51 470.393 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 379.786 M +115.51 379.786 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 289.18 M +115.51 289.18 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 198.573 M +115.51 198.573 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +109 107.966 M +115.51 107.966 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +753.49 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 470.393 M +753.49 470.393 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 379.786 M +753.49 379.786 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 289.18 M +753.49 289.18 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 198.573 M +753.49 198.573 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 107.966 M +753.49 107.966 L +S +GR +GS +[0.75789 0 0 0.75789 78.61052 424.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(0) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61052 356.03486] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61052 287.36445] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(2) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61052 218.69405] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(3) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61052 150.02367] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 78.61052 81.35327] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(5) t +GR +GR +GS +[0 -0.75789 0.75789 0 69.54736 229.92613] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-24 -5 moveto +1 -1 scale +(Time/n) t +GR +GR +GS +[0.75789 0 0 0.75789 82.61052 32.11578] CT +0.149 GC +N +/f-838442823{1.906 -0.375 M +1.906 -0.469 1.969 -0.563 QT +4.672 -3.25 L +1.969 -5.953 L +1.906 -6.016 1.906 -6.125 QT +1.906 -6.219 1.984 -6.305 QT +2.063 -6.391 2.172 -6.391 QT +2.266 -6.391 2.375 -6.297 QT +5.063 -3.609 L +7.734 -6.297 L +7.844 -6.391 7.922 -6.391 QT +8.031 -6.391 8.109 -6.313 QT +8.188 -6.234 8.188 -6.125 QT +8.188 -6.016 8.125 -5.953 QT +5.422 -3.25 L +8.125 -0.563 L +8.188 -0.469 8.188 -0.375 QT +8.188 -0.266 8.109 -0.188 QT +8.031 -0.109 7.922 -0.109 QT +7.828 -0.109 7.734 -0.203 QT +5.063 -2.891 L +2.375 -0.203 L +2.266 -0.109 2.172 -0.109 QT +2.063 -0.109 1.984 -0.195 QT +1.906 -0.281 1.906 -0.375 QT +cp}def +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 90.94736 32.11578] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 103.07368 27.56841] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(-6) t +GR +GR +GS +[0.75789 0 0 0.75789 329.30556 33.06313] CT +/Helvetica-Bold 14.514 F +GS +[1 0 0 1 0 0] CT +-30 -5 moveto +1 -1 scale +(Remove) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +109 82.898 M +271.75 64.807 L +434.5 58.652 L +597.25 50.657 L +760 50.276 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 62.35446] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 205.95788 48.64339] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 329.30524 43.97798] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 452.6526 37.91909] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 575.99996 37.63029] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +109 76.254 M +271.75 65.88 L +434.5 57.944 L +597.25 50.113 L +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 57.31863] CT +0.851 0.325 0.098 RC +N +0 -3.628 M +2.004 -3.628 3.628 -2.004 3.628 0 C +3.628 0 L +3.628 2.004 2.004 3.628 0 3.628 C +-2.004 3.628 -3.628 2.004 -3.628 0 C +-3.628 -2.004 -2.004 -3.628 0 -3.628 C +cp +0 -4.288 M +-2.368 -4.288 -4.288 -2.368 -4.288 0 C +-4.288 2.368 -2.368 4.288 0 4.288 C +2.368 4.288 4.288 2.368 4.288 0 C +4.288 0 L +4.288 -2.368 2.368 -4.288 0 -4.288 C +cp +f +GR +GS +[0.75789 0 0 0.75789 205.95788 49.45607] CT +0.851 0.325 0.098 RC +N +/f-1825166512{0 -3.628 M +2.004 -3.628 3.628 -2.004 3.628 0 C +3.628 0 L +3.628 2.004 2.004 3.628 0 3.628 C +-2.004 3.628 -3.628 2.004 -3.628 0 C +-3.628 -2.004 -2.004 -3.628 0 -3.628 C +cp +0 -4.288 M +-2.368 -4.288 -4.288 -2.368 -4.288 0 C +-4.288 2.368 -2.368 4.288 0 4.288 C +2.368 4.288 4.288 2.368 4.288 0 C +4.288 0 L +4.288 -2.368 2.368 -4.288 0 -4.288 C +cp}def +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 329.30524 43.44149] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 452.6526 37.50695] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 575.99996 35.14736] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +109 258.675 M +271.75 246.646 L +434.5 234.379 L +597.25 199.451 L +760 219.477 L +S +GR +GS +[0.75789 0 0 0.75789 82.61052 195.57502] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 205.95788 186.45826] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 329.30524 177.16099] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 452.6526 150.68944] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 575.99996 165.86704] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +749 125 M +749 58 L +657 58 L +657 125 L +cp +f +GR +GS +[0.75789 0 0 0.75789 535.83154 57.8842] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(T1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 555.5368 54.09473] CT +/Helvetica 9.5 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(2) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +662.995 70.904 M +702.542 70.904 L +S +GR +GS +[0.75789 0 0 0.75789 517.46647 53.26385] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 535.83154 73.79999] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(M1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 558.56838 70.01052] CT +/Helvetica 9.5 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(2) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +662.995 91.5 M +702.542 91.5 L +S +GR +GS +[0.75789 0 0 0.75789 517.46647 68.87368] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 535.83154 88.95789] CT +/Helvetica 11.875 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(A1/n) t +GR +GR +GS +[0.75789 0 0 0.75789 556.29469 85.16841] CT +/Helvetica 9.5 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(2) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +662.995 112.096 M +702.542 112.096 L +S +GR +GS +[0.75789 0 0 0.75789 517.46647 84.4835] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +10.0 ML +0.66 LW +N +657 125 M +657 58 L +749 58 L +749 125 L +cp +S +GR +%%Trailer +%%Pages: 1 +%%EOF diff --git a/OU3/fig2.pdf b/OU3/fig2.pdf new file mode 100644 index 0000000..779184a Binary files /dev/null and b/OU3/fig2.pdf differ diff --git a/OU3/fig3.eps b/OU3/fig3.eps new file mode 100644 index 0000000..9940b42 --- /dev/null +++ b/OU3/fig3.eps @@ -0,0 +1,3133 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: (MATLAB, The Mathworks, Inc. Version 24.2.0.2773142 \(R2024b\) Update 2. Operating System: Linux) +%%Title: ./fig3.eps +%%CreationDate: 2025-05-08T20:51:26 +%%Pages: (atend) +%%BoundingBox: 50 18 577 457 +%%LanguageLevel: 3 +%%EndComments +%%BeginProlog +%%BeginResource: procset (Apache XML Graphics Std ProcSet) 1.2 0 +%%Version: 1.2 0 +%%Copyright: (Copyright 2001-2003,2010 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/bd{bind def}bind def +/ld{load def}bd +/GR/grestore ld +/GS/gsave ld +/RM/rmoveto ld +/C/curveto ld +/t/show ld +/L/lineto ld +/ML/setmiterlimit ld +/CT/concat ld +/f/fill ld +/N/newpath ld +/S/stroke ld +/CC/setcmykcolor ld +/A/ashow ld +/cp/closepath ld +/RC/setrgbcolor ld +/LJ/setlinejoin ld +/GC/setgray ld +/LW/setlinewidth ld +/M/moveto ld +/re {4 2 roll M +1 index 0 rlineto +0 exch rlineto +neg 0 rlineto +cp } bd +/_ctm matrix def +/_tm matrix def +/BT { _ctm currentmatrix pop matrix _tm copy pop 0 0 moveto } bd +/ET { _ctm setmatrix } bd +/iTm { _ctm setmatrix _tm concat } bd +/Tm { _tm astore pop iTm 0 0 moveto } bd +/ux 0.0 def +/uy 0.0 def +/F { + /Tp exch def + /Tf exch def + Tf findfont Tp scalefont setfont + /cf Tf def /cs Tp def +} bd +/ULS {currentpoint /uy exch def /ux exch def} bd +/ULE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add moveto Tcx uy To add lineto + Tt setlinewidth stroke + grestore +} bd +/OLE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs add moveto Tcx uy To add cs add lineto + Tt setlinewidth stroke + grestore +} bd +/SOE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto + Tt setlinewidth stroke + grestore +} bd +/QT { +/Y22 exch store +/X22 exch store +/Y21 exch store +/X21 exch store +currentpoint +/Y21 load 2 mul add 3 div exch +/X21 load 2 mul add 3 div exch +/X21 load 2 mul /X22 load add 3 div +/Y21 load 2 mul /Y22 load add 3 div +/X22 load /Y22 load curveto +} bd +/SSPD { +dup length /d exch dict def +{ +/v exch def +/k exch def +currentpagedevice k known { +/cpdv currentpagedevice k get def +v cpdv ne { +/upd false def +/nullv v type /nulltype eq def +/nullcpdv cpdv type /nulltype eq def +nullv nullcpdv or +{ +/upd true def +} { +/sametype v type cpdv type eq def +sametype { +v type /arraytype eq { +/vlen v length def +/cpdvlen cpdv length def +vlen cpdvlen eq { +0 1 vlen 1 sub { +/i exch def +/obj v i get def +/cpdobj cpdv i get def +obj cpdobj ne { +/upd true def +exit +} if +} for +} { +/upd true def +} ifelse +} { +v type /dicttype eq { +v { +/dv exch def +/dk exch def +/cpddv cpdv dk get def +dv cpddv ne { +/upd true def +exit +} if +} forall +} { +/upd true def +} ifelse +} ifelse +} if +} ifelse +upd true eq { +d k v put +} if +} if +} if +} forall +d length 0 gt { +d setpagedevice +} if +} bd +/RE { % /NewFontName [NewEncodingArray] /FontName RE - + findfont dup length dict begin + { + 1 index /FID ne + {def} {pop pop} ifelse + } forall + /Encoding exch def + /FontName 1 index def + currentdict definefont pop + end +} bind def +%%EndResource +%%BeginResource: procset (Apache XML Graphics EPS ProcSet) 1.0 0 +%%Version: 1.0 0 +%%Copyright: (Copyright 2002-2003 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/BeginEPSF { %def +/b4_Inc_state save def % Save state for cleanup +/dict_count countdictstack def % Count objects on dict stack +/op_count count 1 sub def % Count objects on operand stack +userdict begin % Push userdict on dict stack +/showpage { } def % Redefine showpage, { } = null proc +0 setgray 0 setlinecap % Prepare graphics state +1 setlinewidth 0 setlinejoin +10 setmiterlimit [ ] 0 setdash newpath +/languagelevel where % If level not equal to 1 then +{pop languagelevel % set strokeadjust and +1 ne % overprint to their defaults. +{false setstrokeadjust false setoverprint +} if +} if +} bd +/EndEPSF { %def +count op_count sub {pop} repeat % Clean up stacks +countdictstack dict_count sub {end} repeat +b4_Inc_state restore +} bd +%%EndResource +%FOPBeginFontDict +%%IncludeResource: font Courier-Oblique +%%IncludeResource: font Courier-BoldOblique +%%IncludeResource: font Courier-Bold +%%IncludeResource: font ZapfDingbats +%%IncludeResource: font Symbol +%%IncludeResource: font Helvetica +%%IncludeResource: font Helvetica-Oblique +%%IncludeResource: font Helvetica-Bold +%%IncludeResource: font Helvetica-BoldOblique +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Italic +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-BoldItalic +%%IncludeResource: font Courier +%FOPEndFontDict +%%BeginResource: encoding WinAnsiEncoding +/WinAnsiEncoding [ +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /space /exclam /quotedbl +/numbersign /dollar /percent /ampersand /quotesingle +/parenleft /parenright /asterisk /plus /comma +/hyphen /period /slash /zero /one +/two /three /four /five /six +/seven /eight /nine /colon /semicolon +/less /equal /greater /question /at +/A /B /C /D /E +/F /G /H /I /J +/K /L /M /N /O +/P /Q /R /S /T +/U /V /W /X /Y +/Z /bracketleft /backslash /bracketright /asciicircum +/underscore /quoteleft /a /b /c +/d /e /f /g /h +/i /j /k /l /m +/n /o /p /q /r +/s /t /u /v /w +/x /y /z /braceleft /bar +/braceright /asciitilde /bullet /Euro /bullet +/quotesinglbase /florin /quotedblbase /ellipsis /dagger +/daggerdbl /circumflex /perthousand /Scaron /guilsinglleft +/OE /bullet /Zcaron /bullet /bullet +/quoteleft /quoteright /quotedblleft /quotedblright /bullet +/endash /emdash /asciitilde /trademark /scaron +/guilsinglright /oe /bullet /zcaron /Ydieresis +/space /exclamdown /cent /sterling /currency +/yen /brokenbar /section /dieresis /copyright +/ordfeminine /guillemotleft /logicalnot /sfthyphen /registered +/macron /degree /plusminus /twosuperior /threesuperior +/acute /mu /paragraph /middot /cedilla +/onesuperior /ordmasculine /guillemotright /onequarter /onehalf +/threequarters /questiondown /Agrave /Aacute /Acircumflex +/Atilde /Adieresis /Aring /AE /Ccedilla +/Egrave /Eacute /Ecircumflex /Edieresis /Igrave +/Iacute /Icircumflex /Idieresis /Eth /Ntilde +/Ograve /Oacute /Ocircumflex /Otilde /Odieresis +/multiply /Oslash /Ugrave /Uacute /Ucircumflex +/Udieresis /Yacute /Thorn /germandbls /agrave +/aacute /acircumflex /atilde /adieresis /aring +/ae /ccedilla /egrave /eacute /ecircumflex +/edieresis /igrave /iacute /icircumflex /idieresis +/eth /ntilde /ograve /oacute /ocircumflex +/otilde /odieresis /divide /oslash /ugrave +/uacute /ucircumflex /udieresis /yacute /thorn +/ydieresis +] def +%%EndResource +%FOPBeginFontReencode +/Courier-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Oblique exch definefont pop +/Courier-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-BoldOblique exch definefont pop +/Courier-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Bold exch definefont pop +/Helvetica findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica exch definefont pop +/Helvetica-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Oblique exch definefont pop +/Helvetica-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Bold exch definefont pop +/Helvetica-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-BoldOblique exch definefont pop +/Times-Roman findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Roman exch definefont pop +/Times-Italic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Italic exch definefont pop +/Times-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Bold exch definefont pop +/Times-BoldItalic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-BoldItalic exch definefont pop +/Courier findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier exch definefont pop +%FOPEndFontReencode +%%EndProlog +%%Page: 1 1 +%%PageBoundingBox: 0 0 637 477 +%%BeginPageSetup +N + 50 18 M + 627 18 L + 627 475 L + 50 475 L +cp +clip +[1 0 0 -1 0 477] CT +%%EndPageSetup +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +N +0 0 M +840 0 L +840 630 L +0 630 L +0 0 L +cp +clip +1 GC +N +0 0 840 630 re +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +0 0 840 630 re +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +111 561 M +288 561 L +288 47 L +111 47 L +cp +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 561 M +288 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 47 M +288 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 561 M +111 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +146.4 561 M +146.4 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +181.8 561 M +181.8 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +217.2 561 M +217.2 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +252.6 561 M +252.6 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 47 M +111 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +146.4 47 M +146.4 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +181.8 47 M +181.8 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +217.2 47 M +217.2 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +252.6 47 M +252.6 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 84.12631 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-4 15 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 110.95578 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.3) t +GR +GR +GS +[0.75789 0 0 0.75789 137.78526 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 164.61472 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.9) t +GR +GR +GS +[0.75789 0 0 0.75789 191.4442 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(2.2) t +GR +GR +GS +[0.75789 0 0 0.75789 151.20006 444.10527] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-4.5 16 moveto +1 -1 scale +(n) t +GR +GR +GS +[0.75789 0 0 0.75789 192.50525 458.05263] CT +0.149 GC +N +1.906 -0.375 M +1.906 -0.469 1.969 -0.563 QT +4.672 -3.25 L +1.969 -5.953 L +1.906 -6.016 1.906 -6.125 QT +1.906 -6.219 1.984 -6.305 QT +2.063 -6.391 2.172 -6.391 QT +2.266 -6.391 2.375 -6.297 QT +5.063 -3.609 L +7.734 -6.297 L +7.844 -6.391 7.922 -6.391 QT +8.031 -6.391 8.109 -6.313 QT +8.188 -6.234 8.188 -6.125 QT +8.188 -6.016 8.125 -5.953 QT +5.422 -3.25 L +8.125 -0.563 L +8.188 -0.469 8.188 -0.375 QT +8.188 -0.266 8.109 -0.188 QT +8.031 -0.109 7.922 -0.109 QT +7.828 -0.109 7.734 -0.203 QT +5.063 -2.891 L +2.375 -0.203 L +2.266 -0.109 2.172 -0.109 QT +2.063 -0.109 1.984 -0.195 QT +1.906 -0.281 1.906 -0.375 QT +cp +f +GR +GS +[0.75789 0 0 0.75789 200.84209 458.05263] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 212.9684 453.50526] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 561 M +111 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 561 M +288 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 561 M +116.14 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 509.6 M +116.14 509.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 458.2 M +116.14 458.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 406.8 M +116.14 406.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 355.4 M +116.14 355.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 304 M +116.14 304 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 252.6 M +116.14 252.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 201.2 M +116.14 201.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 149.8 M +116.14 149.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 98.4 M +116.14 98.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +111 47 M +116.14 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 561 M +282.86 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 509.6 M +282.86 509.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 458.2 M +282.86 458.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 406.8 M +282.86 406.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 355.4 M +282.86 355.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 304 M +282.86 304 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 252.6 M +282.86 252.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 201.2 M +282.86 201.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 149.8 M +282.86 149.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 98.4 M +282.86 98.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +288 47 M +282.86 47 L +S +GR +GS +[0.75789 0 0 0.75789 80.12631 424.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(0) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 385.74948] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.2) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 346.79369] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.4) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 307.83788] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.6) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 268.8821] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.8) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 229.92631] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 190.97051] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.2) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 152.01473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.4) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 113.05894] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 74.10314] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.8) t +GR +GR +GS +[0.75789 0 0 0.75789 80.12631 35.14737] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(2) t +GR +GR +GS +[0 -0.75789 0.75789 0 62.72631 229.92615] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-24 -5 moveto +1 -1 scale +(Time/n) t +GR +GR +GS +[0.75789 0 0 0.75789 84.12631 32.11578] CT +0.149 GC +N +/f-838442823{1.906 -0.375 M +1.906 -0.469 1.969 -0.563 QT +4.672 -3.25 L +1.969 -5.953 L +1.906 -6.016 1.906 -6.125 QT +1.906 -6.219 1.984 -6.305 QT +2.063 -6.391 2.172 -6.391 QT +2.266 -6.391 2.375 -6.297 QT +5.063 -3.609 L +7.734 -6.297 L +7.844 -6.391 7.922 -6.391 QT +8.031 -6.391 8.109 -6.313 QT +8.188 -6.234 8.188 -6.125 QT +8.188 -6.016 8.125 -5.953 QT +5.422 -3.25 L +8.125 -0.563 L +8.188 -0.469 8.188 -0.375 QT +8.188 -0.266 8.109 -0.188 QT +8.031 -0.109 7.922 -0.109 QT +7.828 -0.109 7.734 -0.203 QT +5.063 -2.891 L +2.375 -0.203 L +2.266 -0.109 2.172 -0.109 QT +2.063 -0.109 1.984 -0.195 QT +1.906 -0.281 1.906 -0.375 QT +cp}def +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 92.46315 32.11578] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 104.58947 27.56841] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(-5) t +GR +GR +GS +[0.75789 0 0 0.75789 151.20013 33.06316] CT +/Helvetica-Bold 14.514 F +GS +[1 0 0 1 0 0] CT +-51 -5 moveto +1 -1 scale +(Failed lookup) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +111 143.675 M +146.4 105.928 L +181.8 107.853 L +217.2 88.41 L +252.6 89.886 L +S +GR +GS +[0.75789 0 0 0.75789 84.12631 108.41671] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 110.95578 79.80823] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 137.78526 81.26788] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 164.61472 66.53141] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 191.4442 67.65075] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +111 136.35 M +146.4 110.135 L +181.8 108.506 L +217.2 86.001 L +252.6 87.833 L +S +GR +GS +[0.75789 0 0 0.75789 84.12631 102.86549] CT +0.851 0.325 0.098 RC +N +0 -3.628 M +2.004 -3.628 3.628 -2.004 3.628 0 C +3.628 0 L +3.628 2.004 2.004 3.628 0 3.628 C +-2.004 3.628 -3.628 2.004 -3.628 0 C +-3.628 -2.004 -2.004 -3.628 0 -3.628 C +cp +0 -4.288 M +-2.368 -4.288 -4.288 -2.368 -4.288 0 C +-4.288 2.368 -2.368 4.288 0 4.288 C +2.368 4.288 4.288 2.368 4.288 0 C +4.288 0 L +4.288 -2.368 2.368 -4.288 0 -4.288 C +cp +f +GR +GS +[0.75789 0 0 0.75789 110.95578 82.9969] CT +0.851 0.325 0.098 RC +N +/f-1825166512{0 -3.628 M +2.004 -3.628 3.628 -2.004 3.628 0 C +3.628 0 L +3.628 2.004 2.004 3.628 0 3.628 C +-2.004 3.628 -3.628 2.004 -3.628 0 C +-3.628 -2.004 -2.004 -3.628 0 -3.628 C +cp +0 -4.288 M +-2.368 -4.288 -4.288 -2.368 -4.288 0 C +-4.288 2.368 -2.368 4.288 0 4.288 C +2.368 4.288 4.288 2.368 4.288 0 C +4.288 0 L +4.288 -2.368 2.368 -4.288 0 -4.288 C +cp}def +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 137.78526 81.76241] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 164.61472 64.70591] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 191.4442 66.09466] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +111 320.619 M +146.4 325.848 L +181.8 326.906 L +217.2 327.351 L +252.6 326.735 L +S +GR +GS +[0.75789 0 0 0.75789 84.12631 242.52201] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 110.95578 246.48444] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 137.78526 247.28649] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 164.61472 247.62369] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 191.4442 247.15727] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +347 561 M +524 561 L +524 47 L +347 47 L +cp +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 561 M +524 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 47 M +524 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 561 M +347 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +382.4 561 M +382.4 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +417.8 561 M +417.8 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +453.2 561 M +453.2 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +488.6 561 M +488.6 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 47 M +347 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +382.4 47 M +382.4 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +417.8 47 M +417.8 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +453.2 47 M +453.2 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +488.6 47 M +488.6 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 262.98945 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-4 15 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 289.81892 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.3) t +GR +GR +GS +[0.75789 0 0 0.75789 316.64839 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 343.47788 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.9) t +GR +GR +GS +[0.75789 0 0 0.75789 370.30735 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(2.2) t +GR +GR +GS +[0.75789 0 0 0.75789 330.0632 444.10527] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-4.5 16 moveto +1 -1 scale +(n) t +GR +GR +GS +[0.75789 0 0 0.75789 371.36839 458.05263] CT +0.149 GC +N +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 379.70523 458.05263] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 391.83155 453.50526] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 561 M +347 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 561 M +524 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 561 M +352.14 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 509.6 M +352.14 509.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 458.2 M +352.14 458.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 406.8 M +352.14 406.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 355.4 M +352.14 355.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 304 M +352.14 304 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 252.6 M +352.14 252.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 201.2 M +352.14 201.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 149.8 M +352.14 149.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 98.4 M +352.14 98.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +347 47 M +352.14 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 561 M +518.86 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 509.6 M +518.86 509.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 458.2 M +518.86 458.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 406.8 M +518.86 406.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 355.4 M +518.86 355.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 304 M +518.86 304 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 252.6 M +518.86 252.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 201.2 M +518.86 201.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 149.8 M +518.86 149.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 98.4 M +518.86 98.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +524 47 M +518.86 47 L +S +GR +GS +[0.75789 0 0 0.75789 258.98946 424.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(0) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 385.74948] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.2) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 346.79369] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.4) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 307.83788] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.6) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 268.8821] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.8) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 229.92631] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 190.97051] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.2) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 152.01473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.4) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 113.05894] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 74.10314] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.8) t +GR +GR +GS +[0.75789 0 0 0.75789 258.98946 35.14737] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(2) t +GR +GR +GS +[0 -0.75789 0.75789 0 241.58945 229.92615] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-24 -5 moveto +1 -1 scale +(Time/n) t +GR +GR +GS +[0.75789 0 0 0.75789 262.98945 32.11578] CT +0.149 GC +N +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 271.3263 32.11578] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 283.45261 27.56841] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(-5) t +GR +GR +GS +[0.75789 0 0 0.75789 330.06346 33.06316] CT +/Helvetica-Bold 14.514 F +GS +[1 0 0 1 0 0] CT +-59.5 -5 moveto +1 -1 scale +(Random lookup) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +347 342.807 M +382.4 319.537 L +417.8 312.048 L +453.2 303.229 L +488.6 304.85 L +S +GR +GS +[0.75789 0 0 0.75789 262.98945 259.33794] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 289.81892 241.70141] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 316.64839 236.02583] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 343.47788 229.34179] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 370.30735 230.5702] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +347 325.716 M +382.4 306.889 L +417.8 300.252 L +453.2 287.389 L +488.6 289.389 L +S +GR +GS +[0.75789 0 0 0.75789 262.98945 246.38513] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 289.81892 232.11613] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 316.64839 227.08578] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 343.47788 217.33672] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 370.30735 218.85262] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +347 440.253 M +382.4 445.097 L +417.8 443.928 L +453.2 445.089 L +488.6 444.164 L +S +GR +GS +[0.75789 0 0 0.75789 262.98945 333.19162] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 289.81892 336.86265] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 316.64839 335.97685] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 343.47788 336.85689] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 370.30735 336.15596] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +1 GC +N +583 561 M +760 561 L +760 47 L +583 47 L +cp +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 561 M +760 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 47 M +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 561 M +583 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +618.4 561 M +618.4 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +653.8 561 M +653.8 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +689.2 561 M +689.2 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +724.6 561 M +724.6 555.86 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 47 M +583 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +618.4 47 M +618.4 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +653.8 47 M +653.8 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +689.2 47 M +689.2 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +724.6 47 M +724.6 52.14 L +S +GR +GS +[0.75789 0 0 0.75789 441.8526 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-4 15 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 468.68209 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.3) t +GR +GR +GS +[0.75789 0 0 0.75789 495.51153 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 522.34102 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(1.9) t +GR +GR +GS +[0.75789 0 0 0.75789 549.17047 428.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-9.5 15 moveto +1 -1 scale +(2.2) t +GR +GR +GS +[0.75789 0 0 0.75789 508.92637 444.10527] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-4.5 16 moveto +1 -1 scale +(n) t +GR +GR +GS +[0.75789 0 0 0.75789 550.23154 458.05263] CT +0.149 GC +N +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 558.56838 458.05263] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 570.69469 453.50526] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(4) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 561 M +583 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +760 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 561 M +588.14 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 509.6 M +588.14 509.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 458.2 M +588.14 458.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 406.8 M +588.14 406.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 355.4 M +588.14 355.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 304 M +588.14 304 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 252.6 M +588.14 252.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 201.2 M +588.14 201.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 149.8 M +588.14 149.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 98.4 M +588.14 98.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +583 47 M +588.14 47 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 561 M +754.86 561 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 509.6 M +754.86 509.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 458.2 M +754.86 458.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 406.8 M +754.86 406.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 355.4 M +754.86 355.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 304 M +754.86 304 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 252.6 M +754.86 252.6 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 201.2 M +754.86 201.2 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 149.8 M +754.86 149.8 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 98.4 M +754.86 98.4 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.149 GC +2 setlinecap +1 LJ +0.66 LW +N +760 47 M +754.86 47 L +S +GR +GS +[0.75789 0 0 0.75789 437.8526 424.70526] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(0) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 385.74948] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.2) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 346.79369] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.4) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 307.83788] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.6) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 268.8821] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(0.8) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 229.92631] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(1) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 190.97051] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.2) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 152.01473] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.4) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 113.05894] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.6) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 74.10314] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-19 5.5 moveto +1 -1 scale +(1.8) t +GR +GR +GS +[0.75789 0 0 0.75789 437.8526 35.14737] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +-8 5.5 moveto +1 -1 scale +(2) t +GR +GR +GS +[0 -0.75789 0.75789 0 420.45262 229.92615] CT +0.149 GC +/Helvetica 14.514 F +GS +[1 0 0 1 0 0] CT +-24 -5 moveto +1 -1 scale +(Time/n) t +GR +GR +GS +[0.75789 0 0 0.75789 441.8526 32.11578] CT +0.149 GC +N +f-838442823 +f +GR +GS +[0.75789 0 0 0.75789 450.18944 32.11578] CT +0.149 GC +/Helvetica 13.194 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(10) t +GR +GR +GS +[0.75789 0 0 0.75789 462.31575 27.56841] CT +0.149 GC +/Helvetica 10.556 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(-5) t +GR +GR +GS +[0.75789 0 0 0.75789 508.92679 33.06316] CT +/Helvetica-Bold 14.514 F +GS +[1 0 0 1 0 0] CT +-57 -5 moveto +1 -1 scale +(Skewed lookup) t +GR +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0 0.447 0.741 RC +1 LJ +0.66 LW +N +583 335.997 M +618.4 315.253 L +653.8 306.844 L +689.2 302.707 L +724.6 299.54 L +S +GR +GS +[0.75789 0 0 0.75789 441.8526 254.17631] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 468.68209 238.4551] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 495.51153 232.08206] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 522.34102 228.94612] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 549.17047 226.54585] CT +0 0.447 0.741 RC +10.0 ML +0.66 LW +N +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.851 0.325 0.098 RC +1 LJ +0.66 LW +N +583 441.024 M +618.4 428.369 L +653.8 419.667 L +689.2 414.667 L +724.6 410.844 L +S +GR +GS +[0.75789 0 0 0.75789 441.8526 333.77596] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 468.68209 324.18473] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 495.51153 317.58953] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 522.34102 313.79997] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 549.17047 310.90311] CT +0.851 0.325 0.098 RC +N +f-1825166512 +f +GR +GS +[0.75789 0 0 0.75789 0 -0.47369] CT +0.929 0.694 0.125 RC +1 LJ +0.66 LW +N +583 440.296 M +618.4 443.044 L +653.8 444.346 L +689.2 444.294 L +724.6 444.801 L +S +GR +GS +[0.75789 0 0 0.75789 441.8526 333.22407] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 468.68209 335.30671] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 495.51153 336.29388] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 522.34102 336.2544] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +GS +[0.75789 0 0 0.75789 549.17047 336.63888] CT +0.929 0.694 0.125 RC +10.0 ML +0.66 LW +N +0 -4 M +0 4 L +-4 0 M +4 0 L +-2.799 -2.799 M +2.799 2.799 L +-2.799 2.799 M +2.799 -2.799 L +S +GR +%%Trailer +%%Pages: 1 +%%EOF diff --git a/OU3/fig3.pdf b/OU3/fig3.pdf new file mode 100644 index 0000000..4c277ae Binary files /dev/null and b/OU3/fig3.pdf differ diff --git a/OU3/include/array_1d.h b/OU3/include/array_1d.h new file mode 100644 index 0000000..95faae1 --- /dev/null +++ b/OU3/include/array_1d.h @@ -0,0 +1,150 @@ +#ifndef __ARRAY_1D_H +#define __ARRAY_1D_H + +#include +#include "util.h" + +/* + * Declaration of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The array stores void pointers, so it can be used to + * store all types of values. After use, the function array_kill must + * be called to de-allocate the dynamic memory used by the array + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the array, + * unless a kill_function is registered in array_create. + * + * An element value of NULL is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct array_1d array_1d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_1d *array_1d_create(int lo, int hi, kill_function kill_func); + +/** + * array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int array_1d_low(const array_1d *a); + +/** + * array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int array_1d_high(const array_1d *a); + +/** + * array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_1d_inspect_value(const array_1d *a, int i); + +/** + * array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_1d_has_value(const array_1d *a, int i); + +/** + * array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_1d_set_value(array_1d *a, void *v, int i); + +/** + * array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_1d_kill(array_1d *a); + +/** + * array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_1d_print(const array_1d * l, inspect_callback print_func); + +/** + * array_1d_print_internal() - Print the internal structure of the array in dot format. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/array_2d.h b/OU3/include/array_2d.h new file mode 100644 index 0000000..3020ece --- /dev/null +++ b/OU3/include/array_2d.h @@ -0,0 +1,158 @@ +#ifndef __ARRAY_2D_H +#define __ARRAY_2D_H + +#include +#include "util.h" + +/* + * Declaration of a generic 2D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The array stores void pointers, so it can be used to + * store all types of values. After use, the function array_kill must + * be called to de-allocate the dynamic memory used by the array + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the array, + * unless a kill_function is registered in array_create. + * + * An element value of NULL is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Moved freehandler to last in create parameter list. + * v1.2 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct array_2d array_2d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * array_2d_create() - Create an array without values. + * @lo1: low index limit for first dimension. + * @hi1: high index limit for first dimension. + * @lo2: low index limit for second dimension. + * @hi2: high index limit for second dimension. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2, + kill_function kill_func); + +/** + * array_2d_low() - Return the low index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The low index limit for dimension number d. + */ +int array_2d_low(const array_2d *a, int d); + +/** + * array_2d_high() - Return the high index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The high index limit for dimension number d. + */ +int array_2d_high(const array_2d *a, int d); + +/** + * array_2d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_2d_inspect_value(const array_2d *a, int i, int j); + +/** + * array_2d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_2d_has_value(const array_2d *a, int i, int j); + +/** + * array_2d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_2d_set_value(array_2d *a, void *v, int i, int j); + +/** + * array_2d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_2d_kill(array_2d *a); + +/** + * array_2d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_2d_print(const array_2d * l, inspect_callback print_func); + +/** + * array_2d_print_internal() - Print the arrays internal structure. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/dlist.h b/OU3/include/dlist.h new file mode 100644 index 0000000..8900bc8 --- /dev/null +++ b/OU3/include/dlist.h @@ -0,0 +1,199 @@ +#ifndef __DLIST_H +#define __DLIST_H + +#include +#include "util.h" + +/* + * Declaration of a generic, directed list for the "Datastructures + * and algorithms" courses at the Department of Computing Science, + * Umea University. The list stores void pointers, so it can be used + * to store all types of values. After use, the function list_kill + * must be called to de-allocate the dynamic memory used by the list + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the list, + * unless a kill_function is registered in list_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added dlist_pos_equal and dlist_pos_is_valid functions. + * v1.2 2023-01-20: Renamed dlist_pos_equal to dlist_pos_are_equal. + * v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct dlist dlist; + +// List position type. +typedef struct cell *dlist_pos; + + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * dlist_empty() - Create an empty dlist. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +dlist *dlist_empty(kill_function kill_func); + +/** + * dlist_is_empty() - Check if a dlist is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool dlist_is_empty(const dlist *l); + +/** + * dlist_first() - Return the first position of a dlist, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +dlist_pos dlist_first(const dlist *l); + +/** + * dlist_next() - Return the next position in a dlist. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +dlist_pos dlist_next(const dlist *l, const dlist_pos p); + +/** + * dlist_is_end() - Check if a given position is at the end of a dlist. + * @l: List to inspect. + * @p: Any valid position in the list. + * + * Returns: True if p is at the end of the list. + */ +bool dlist_is_end(const dlist *l, const dlist_pos p); + +/** + * dlist_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *dlist_inspect(const dlist *l, const dlist_pos p); + +/** + * dlist_insert() - Insert a new element with a given value into a dlist. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p); + +/** + * dlist_remove() - Remove an element from a dlist. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +dlist_pos dlist_remove(dlist *l, const dlist_pos p); + +/** + * dlist_kill() - Destroy a given dlist. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void dlist_kill(dlist *l); + +/** + * dlist_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void dlist_print(const dlist *l, inspect_callback print_func); + +/** + * dlist_pos_is_valid() - Return true for a valid position in a dlist + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool dlist_pos_is_valid(const dlist *l, const dlist_pos p); + +/** + * dlist_pos_is_equal() - Return true if two positions in a dlist are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2); + +/** + * dlist_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/int_array_1d.h b/OU3/include/int_array_1d.h new file mode 100644 index 0000000..9b53387 --- /dev/null +++ b/OU3/include/int_array_1d.h @@ -0,0 +1,136 @@ +#ifndef __INT_ARRAY_1D_H +#define __INT_ARRAY_1D_H + +#include +#include "util.h" + +/* + * Declaration of an integer 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. After use, the function array_kill must be called to + * de-allocate the dynamic memory used by the array itself. + * + * An element value of 0 is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.2 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct int_array_1d int_array_1d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * int_array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +int_array_1d *int_array_1d_create(int lo, int hi); + +/** + * int_array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int int_array_1d_low(const int_array_1d *a); + +/** + * int_array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int int_array_1d_high(const int_array_1d *a); + +/** + * int_array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position, or 0 if no + * value is stored at that position. + */ +int int_array_1d_inspect_value(const int_array_1d *a, int i); + +/** + * int_array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool int_array_1d_has_value(const int_array_1d *a, int i); + +/** + * int_array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or 0 to clear value. + * @i: index of position to modify. + * + * Returns: Nothing. + */ +void int_array_1d_set_value(int_array_1d *a, int v, int i); + +/** + * int_array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If free_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void int_array_1d_kill(int_array_1d *a); + +/** + * int_array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * + * Iterates over each position in the array. Prints each non-zero element. + * + * Returns: Nothing. + */ +void int_array_1d_print(const int_array_1d *a); + +/** + * int_array_1d_print_internal() - Print the arrays internal structure in dot format. + * @a: Array to inspect. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level); + +#endif diff --git a/OU3/include/int_list.h b/OU3/include/int_list.h new file mode 100644 index 0000000..30e9549 --- /dev/null +++ b/OU3/include/int_list.h @@ -0,0 +1,192 @@ +#ifndef __INT_LIST_H +#define __INT_LIST_H + +#include +#include "util.h" + +/* + * Declaration of a undirected list for storing integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * dynamic, linked structure. After use, the function list_kill must + * be called to de-allocate the dynamic memory used by the list + * itself. The implementation is a written a code copy specialization + * of the generic list to provide a simpler starting data structure + * than the generic list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef struct cell *list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @data: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, int data, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * + * Iterates over the list and prints each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list *l); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level); + +#endif diff --git a/OU3/include/int_list_array.h b/OU3/include/int_list_array.h new file mode 100644 index 0000000..9db2f74 --- /dev/null +++ b/OU3/include/int_list_array.h @@ -0,0 +1,193 @@ +#ifndef __INT_LIST_ARRAY_H +#define __INT_LIST_ARRAY_H + +#include +#include "util.h" + +/* + * Declaration of a undirected list for storing integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * static array. After use, the function list_kill must be called to + * de-allocate the dynamic memory used by the list itself. The + * implementation is a written a code copy specialization of the + * generic list to provide a simpler starting data structure than the + * generic list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-03-26: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * Bugfix in list_remove. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef int list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @data: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, int data, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * + * Iterates over the list and prints each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list *l); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level); + +#endif diff --git a/OU3/include/int_queue.h b/OU3/include/int_queue.h new file mode 100644 index 0000000..45a8c2e --- /dev/null +++ b/OU3/include/int_queue.h @@ -0,0 +1,126 @@ +#ifndef __INT_QUEUE_H +#define __INT_QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of an integer queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores integers directly and does not use + * dynamic memory. Thus, the clean-up function queue_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic queue implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +// ==========PUBLIC CONSTANTS AND DATA TYPES============ + +// Maximum size of the queue. +#define MAX_QUEUE_SIZE 100 + +// We must define the struct publically for the compiler to know its +// size for copying, etc. +typedef struct queue { + int first_free_pos; + int elements[MAX_QUEUE_SIZE]; +} queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * + * Returns: A new, empty, queue. + */ +queue queue_empty(void); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue q); + +/** + * queue_enqueue() - Put a value at the end of a queue. + * @q: Queue to manipulate. + * @v: Value (integer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue queue_enqueue(queue q, int v); + +/** + * queue_dequeue() - Remove the element at the beginning of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue queue_dequeue(queue q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the front of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +int queue_front(const queue q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Does nothing since the queue does not use any dynamic + * memory. Included for symmetry with generic queue.h. + * + * Returns: Nothing. + */ +void queue_kill(queue q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * + * Iterates over the queue and prints each integer. + * + * Returns: Nothing. + */ +void queue_print(const queue q); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the queue and outputs dot code that shows the + * internal structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems); + +#endif diff --git a/OU3/include/int_stack.h b/OU3/include/int_stack.h new file mode 100644 index 0000000..a64cb28 --- /dev/null +++ b/OU3/include/int_stack.h @@ -0,0 +1,134 @@ +#ifndef __INT_STACK_H +#define __INT_STACK_H + +#include +#include "util.h" + +/* + * Declaration of an integer stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores integers directly and does not use + * dynamic memory. Thus, the clean-up function stack_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic stack implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-04-15: Reduced default stack size to 15 to make output + * from print_internal readable. + * v1.2 2024-05-10: Updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Stack type. +#define MAX_STACK_SIZE 100 + +// We must define the struct publically for the compiler to know its +// size for copying, etc. +typedef struct stack { + int first_free_pos; + int elements[MAX_STACK_SIZE]; +} stack; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * stack_empty() - Create an empty stack. + * + * Returns: A new stack. + */ +stack stack_empty(void); + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack s); + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (integer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_push(stack s, int v); + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_pop(stack s); + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +int stack_top(const stack s); + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Does nothing since the stack does not use any dynamic + * memory. Included for symmetry with generic stack.h. + * + * Returns: Nothing. + */ +void stack_kill(stack s); + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * + * Iterates over the stack and prints each integer. + * + * Returns: Nothing. + */ +void stack_print(const stack s); + +/** + * stack_print_internal() - Print the internal structure of the stack in dot format. + * @s: Stack to inspect. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the stack and outputs dot code that shows the + * internal structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems); + +#endif diff --git a/OU3/include/list.h b/OU3/include/list.h new file mode 100644 index 0000000..a65d7bd --- /dev/null +++ b/OU3/include/list.h @@ -0,0 +1,207 @@ +#ifndef __LIST_H +#define __LIST_H + +#include +#include "util.h" + +/* + * Declaration of a generic, undirected list for the "Datastructures + * and algorithms" courses at the Department of Computing Science, + * Umea University. The list stores void pointers, so it can be used + * to store all types of values. After use, the function list_kill + * must be called to de-allocate the dynamic memory used by the list + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the list, + * unless a kill_function is registered in list_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v2.0 2024-03-14: Added list_print_internal to output dot code for visualization. + * v2.1 2024-05-10: Updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef struct cell *list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +list *list_empty(kill_function kill_func); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, void *v, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void list_print(const list *l, inspect_callback print_func); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/queue.h b/OU3/include/queue.h new file mode 100644 index 0000000..1070e98 --- /dev/null +++ b/OU3/include/queue.h @@ -0,0 +1,131 @@ +#ifndef __QUEUE_H +#define __QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores void pointers, so it can be used to + * store all types of values. After use, the function queue_kill must + * be called to de-allocate the dynamic memory used by the queue + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the queue, + * unless a kill_function is registered in queue_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Queue type. +typedef struct queue queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q); + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v); + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/stack.h b/OU3/include/stack.h new file mode 100644 index 0000000..2dfef95 --- /dev/null +++ b/OU3/include/stack.h @@ -0,0 +1,133 @@ +#ifndef __STACK_H +#define __STACK_H + +#include +#include "util.h" + +/* + * Declaration of a generic stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores void pointers, so it can be used to + * store all types of values. After use, the function stack_kill must + * be called to de-allocate the dynamic memory used by the stack + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the stack, + * unless a kill_function is registered in stack_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Stack type. +typedef struct stack stack; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * stack_empty() - Create an empty stack. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new stack. + */ +stack *stack_empty(kill_function kill_func); + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack *s); + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (pointer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_push(stack *s, void *v); + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_pop(stack *s); + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +void *stack_top(const stack *s); + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Return all dynamic memory used by the stack and its elements. If a + * kill_func was registered at stack creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void stack_kill(stack *s); + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * @print_func: Function called for each element. + * + * Iterates over the stack and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void stack_print(const stack *s, inspect_callback print_func); + +/** + * stack_print_internal() - Print the stacks internal structure in dot format. + * @l: Stack to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the stack and outputs dot code that shows the internal + * structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/table.h b/OU3/include/table.h new file mode 100644 index 0000000..1a7d309 --- /dev/null +++ b/OU3/include/table.h @@ -0,0 +1,157 @@ +#ifndef TABLE_H +#define TABLE_H + +#include +#include "util.h" + +/* + * Declaration of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The table stores void pointers, so it can be used to + * store all types of values. After use, the function table_kill must + * be called to de-allocate the dynamic memory used by the table + * itself. The de-allocation of any dynamic memory allocated for the + * key and/or value values is the responsibility of the user of the + * table, unless a corresponding kill_function is registered in + * table_empty. + * + * The internal handling of duplicates are transparent to the user of + * the table. Depending on the table design, a duplicate may or may + * not be stored internally. The user of the table should not make any + * assumption about which solution is implemented. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2024-04-15: Changed the comments to leave room for different + * internal handling of duplicates. + * v1.2 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ +// Table type. +typedef struct table table; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. See + * util.h for the definition of compare_function. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func); + +/** + * table_is_empty() - Check if a table is empty. + * @t: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t); + +/** + * table_insert() - Add a key/value pair to a table. + * @t: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. If the key already + * exists, the key/value overwrites the existing pair. The technical + * details are internal to the datatype. At any rate, table_lookup() + * will return the latest added value for a duplicate + * key and table_remove() will remove all duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value); + +/** + * table_lookup() - Look up a given key in a table. + * @t: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key); + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t); + +/** + * table_remove() - Remove a key/value pair in the table. + * @t: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key); + +/** + * table_kill() - Destroy a table. + * @t: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to free any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t); + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including any duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func); + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU3/include/util.h b/OU3/include/util.h new file mode 100644 index 0000000..db3681e --- /dev/null +++ b/OU3/include/util.h @@ -0,0 +1,83 @@ +#ifndef __UTIL_H +#define __UTIL_H + +/* + * Utility function types for deallocating, printing and comparing + * values stored by various data types. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-02-06: Updated explanation for the compare_function. + * v1.2 2023-01-14: Added version define constants and strings. + * v1.3 2024-03-13: Added PTR2ADDR macro. + */ + +// Macros to create a version string out of version constants +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +// Version constants +#define CODE_BASE_MAJOR_VERSION 2 +#define CODE_BASE_MINOR_VERSION 2 +#define CODE_BASE_REVISION 2 +#define CODE_BASE_PATCH 2 + +#define CODE_BASE_RELEASE_DATE "2025-01-24" + +// Create a short version string +#define CODE_BASE_VERSION "v" \ + STR(CODE_BASE_MAJOR_VERSION) \ + "." \ + STR(CODE_BASE_MINOR_VERSION) \ + "." \ + STR(CODE_BASE_REVISION) \ + "." \ + STR(CODE_BASE_PATCH) + +// Create a version string +#define CODE_BASE_LONG_VERSION "Version: " \ + STR(CODE_BASE_MAJOR_VERSION) \ + "." \ + STR(CODE_BASE_MINOR_VERSION) \ + "." \ + STR(CODE_BASE_REVISION) \ + "." \ + STR(CODE_BASE_PATCH) + +// Type definition for de-allocator function, e.g. free(). +typedef void (*kill_function)(void *); + +// For backwards compatibility with pre-v2.0 code. +typedef kill_function free_function; + +// Type definition for read-only callback for single-value containers, +// used by e.g. print functions. +typedef void (*inspect_callback)(const void *); + +// Ditto for dual-value containers. +typedef void (*inspect_callback_pair)(const void *, const void *); + +// Type definition for comparison function, used by e.g. table. +// +// Comparison functions should return values that indicate the order +// of the arguments. If the first argument is considered less/lower +// than the second, a negative value should be returned. If the first +// argument is considered more/higher than the second, a positive value +// should be returned. If the arguments are considered equal, a zero +// value should be returned. +typedef int compare_function(const void *,const void *); + +// Constant used by ptr2addr, used by various print_internal functions. +#define PTR2ADDR_MASK 0xffff + +// Macro that acts as a function to convert the address in the pointer +// p into an unsigned long value, keeping only the part indicated by +// the mask. +#define PTR2ADDR(p) (((unsigned long)p) & PTR2ADDR_MASK) + +#endif diff --git a/OU3/lib/.gitignore b/OU3/lib/.gitignore new file mode 100644 index 0000000..9ae31f6 --- /dev/null +++ b/OU3/lib/.gitignore @@ -0,0 +1 @@ +libdoa.a diff --git a/OU3/lib/Makefile b/OU3/lib/Makefile new file mode 100644 index 0000000..b50c5c1 --- /dev/null +++ b/OU3/lib/Makefile @@ -0,0 +1,34 @@ +SRC = ../src/list/list.c ../src/stack/stack.c \ + ../src/array_2d/array_2d.c ../src/table/table.c \ + ../src/table/table2.c ../src/array_1d/array_1d.c \ + ../src/queue/queue.c ../src/dlist/dlist.c \ + ../src/version/version.c +H = ../include/queue.h ../include/dlist.h ../include/array_2d.h \ + ../include/util.h ../include/table.h ../include/list.h \ + ../include/array_1d.h ../include/stack.h + +OBJ = $(SRC:.c=.o) + +LIB = libdoa.a + +CC = gcc +CFLAGS = -std=c99 -Wall -I../include -g + +all: lib + +# Library +lib: $(LIB) + +# Object file for library +$(LIB): $(OBJ) $(H) + $(AR) r $@ $(OBJ) + ranlib $@ + +# Clean up +clean: + -rm -f $(OBJ) + +# Clean up +cleaner: + -rm -f $(LIB) $(OBJ) + diff --git a/OU3/mtftable.c b/OU3/mtftable.c new file mode 100644 index 0000000..6a24892 --- /dev/null +++ b/OU3/mtftable.c @@ -0,0 +1,697 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table with the slight change of + * inserting looked up value in front of the list for faster + * lookup next time. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Marc Meunier (tfy22mmr@cs.umu.se) + * + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se) + * Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(NULL); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. The looked up + * entry will be placed first in talbe for faster lookup. + */ +void *table_lookup(const table *t, const void *key) +{ + dlist_pos pos = dlist_first(t->entries); + // The value pointer to return. + void *value_ptr = NULL; + //dlist_pos pos_before = pos; + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + value_ptr = e->value; + //pos_before = pos; + pos = dlist_remove(t->entries, pos); + // inserts value in front + dlist_insert(t->entries, e, dlist_first(t->entries)); + return value_ptr; + } + else { + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + } + return value_ptr; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/mtftableprint b/OU3/mtftableprint new file mode 100755 index 0000000..20c36d6 Binary files /dev/null and b/OU3/mtftableprint differ diff --git a/OU3/mtftabletest b/OU3/mtftabletest new file mode 100755 index 0000000..e29d322 Binary files /dev/null and b/OU3/mtftabletest differ diff --git a/OU3/mtftabletest (conflicted copy 2025-05-08 202741) b/OU3/mtftabletest (conflicted copy 2025-05-08 202741) new file mode 100755 index 0000000..9785872 Binary files /dev/null and b/OU3/mtftabletest (conflicted copy 2025-05-08 202741) differ diff --git a/OU3/mtiming.txt b/OU3/mtiming.txt new file mode 100644 index 0000000..7f922eb --- /dev/null +++ b/OU3/mtiming.txt @@ -0,0 +1,150 @@ +1, 10000, 2 +2, 10000, 536 +3, 10000, 1650 +4, 10000, 912 +5, 10000, 459 +1, 10000, 2 +2, 10000, 533 +3, 10000, 1666 +4, 10000, 937 +5, 10000, 463 +1, 10000, 1 +2, 10000, 535 +3, 10000, 1616 +4, 10000, 902 +5, 10000, 459 +1, 10000, 1 +2, 10000, 537 +3, 10000, 1632 +4, 10000, 898 +5, 10000, 465 +1, 10000, 1 +2, 10000, 537 +3, 10000, 1629 +4, 10000, 905 +5, 10000, 466 +1, 10000, 1 +2, 10000, 532 +3, 10000, 1721 +4, 10000, 939 +5, 10000, 489 +1, 13000, 2 +2, 13000, 936 +3, 13000, 3046 +4, 13000, 1701 +5, 13000, 880 +1, 13000, 2 +2, 13000, 909 +3, 13000, 2955 +4, 13000, 1667 +5, 13000, 871 +1, 13000, 1 +2, 13000, 913 +3, 13000, 2906 +4, 13000, 1658 +5, 13000, 870 +1, 13000, 1 +2, 13000, 912 +3, 13000, 3015 +4, 13000, 1677 +5, 13000, 875 +1, 13000, 3 +2, 13000, 957 +3, 13000, 2951 +4, 13000, 1655 +5, 13000, 878 +1, 13000, 2 +2, 13000, 914 +3, 13000, 2916 +4, 13000, 1668 +5, 13000, 859 +1, 16000, 2 +2, 16000, 1416 +3, 16000, 4547 +4, 16000, 2606 +5, 16000, 1422 +1, 16000, 2 +2, 16000, 1427 +3, 16000, 4538 +4, 16000, 2618 +5, 16000, 1419 +1, 16000, 2 +2, 16000, 1428 +3, 16000, 4592 +4, 16000, 2614 +5, 16000, 1413 +1, 16000, 2 +2, 16000, 1419 +3, 16000, 4539 +4, 16000, 2608 +5, 16000, 1407 +1, 16000, 2 +2, 16000, 1421 +3, 16000, 4399 +4, 16000, 2567 +5, 16000, 1405 +1, 16000, 2 +2, 16000, 1417 +3, 16000, 4429 +4, 16000, 2571 +5, 16000, 1381 +1, 19000, 3 +2, 19000, 2034 +3, 19000, 6775 +4, 19000, 3911 +5, 19000, 2092 +1, 19000, 2 +2, 19000, 2064 +3, 19000, 6696 +4, 19000, 3909 +5, 19000, 2107 +1, 19000, 2 +2, 19000, 2065 +3, 19000, 6717 +4, 19000, 3871 +5, 19000, 2051 +1, 19000, 3 +2, 19000, 2030 +3, 19000, 6605 +4, 19000, 3861 +5, 19000, 2032 +1, 19000, 2 +2, 19000, 2035 +3, 19000, 6590 +4, 19000, 3708 +5, 19000, 2013 +1, 19000, 3 +2, 19000, 1985 +3, 19000, 6650 +4, 19000, 3800 +5, 19000, 2038 +1, 22000, 2 +2, 22000, 2684 +3, 22000, 8811 +4, 22000, 5025 +5, 22000, 2769 +1, 22000, 2 +2, 22000, 2732 +3, 22000, 8817 +4, 22000, 5154 +5, 22000, 2813 +1, 22000, 3 +2, 22000, 2839 +3, 22000, 8776 +4, 22000, 5045 +5, 22000, 2780 +1, 22000, 3 +2, 22000, 2740 +3, 22000, 9041 +4, 22000, 5178 +5, 22000, 2897 +1, 22000, 2 +2, 22000, 2729 +3, 22000, 9045 +4, 22000, 5186 +5, 22000, 2873 +1, 22000, 3 +2, 22000, 2750 +3, 22000, 8976 +4, 22000, 5103 +5, 22000, 2835 diff --git a/OU3/parse_timings.m b/OU3/parse_timings.m new file mode 100644 index 0000000..c6fbfbd --- /dev/null +++ b/OU3/parse_timings.m @@ -0,0 +1,95 @@ +load ttiming.txt +load mtiming.txt +load atiming.txt +% 3rd column contains the timing of 5 functions at 5 sizes repeated 6 times +T=reshape(ttiming(:,3),[5,5,6]); +M=reshape(mtiming(:,3),[5,5,6]); +A=reshape(atiming(:,3),[5,5,6]); +% 2nd column contains the problem sizes +n = unique(ttiming(:,2)) +Ta=mean(T,3); +Ma=mean(M,3); +Aa=mean(A,3); + +n1=@(n)n(:)'; +n2=@(n)n(:)'.^2; +n3=@(n)n(:)'.^3; + +figure(1) +set(1,'name','Function #1 = insert') +ix = 1; +T1=mean(reshape(ttiming(ttiming(:,1)==ix,3),6,[]),1); +M1=mean(reshape(mtiming(mtiming(:,1)==ix,3),6,[]),1); +A1=mean(reshape(atiming(atiming(:,1)==ix,3),6,[]),1); +g=n1; + +subplot(2,1,1) +plot(n,T1./g(n),'x-',n,M1./g(n),'o-',n,A1./g(n),'*-') +legend('T1/n','M1/n','A1/n') +set(gca,'ylim',[0,inf],'xtick',n) +title(gca,'Insert') +xlabel("n") +ylabel("Time/n") +subplot(2,1,2) +plot(n,T1./g(n),'x-',n,M1./g(n),'o-') +legend('T1/n','M1/n') +set(gca,'ylim',[0,inf],'xtick',n) +xlabel("n") +ylabel("Time/n") +title(gca,'Insert') +%xlabel("n") +%ylabel("") + +figure(2) +set(2,'name','Function #2 = remove') +ix = 2; +T1=mean(reshape(ttiming(ttiming(:,1)==ix,3),6,[]),1); +M1=mean(reshape(mtiming(mtiming(:,1)==ix,3),6,[]),1); +A1=mean(reshape(atiming(atiming(:,1)==ix,3),6,[]),1); +g=n2; +plot(n,T1./g(n),'x-',n,M1./g(n),'o-',n,A1./g(n),'*-') +legend('T1/n^2','M1/n^2','A1/n^2') +set(gca,'ylim',[0,inf],'xtick',n) +title(gca,'Remove') +xlabel("n") +ylabel("Time/n") + +figure(3) +set(3,'name','Function #3..#5 = various lookup') +titles={'Failed lookup', 'Random lookup', 'Skewed lookup'}; + +for i = 1:3 + ix = 2+i; + T1=mean(reshape(ttiming(ttiming(:,1)==ix,3),6,[]),1); + M1=mean(reshape(mtiming(mtiming(:,1)==ix,3),6,[]),1); + A1=mean(reshape(atiming(atiming(:,1)==ix,3),6,[]),1); + g=n2; + subplot(1,3,i) + plot(n,T1./g(n),'x-',n,M1./g(n),'o-',n,A1./g(n),'*-') + set(gca,'ylim',[0,2e-5],'xtick',n) + title(gca,titles{i}) + xlabel("n") + ylabel("Time/n") +end + + +print -depsc -f1 fig1.eps +print -depsc -f2 fig2.eps +print -depsc -f3 fig3.eps + +disp('You now have three files fig1.eps, fig2.eps, fig3.eps in the /tmp folder') +disp('Under linux, do the following commands to convert them to pdf:') +disp(' ') +disp('cd /tmp') +disp('epspdf fig1.eps') +disp('epspdf fig2.eps') +disp('epspdf fig3.eps') +disp(' ') +disp('The copy the pdf files to your latex folder or insert into overleaf.') + +% Then run the following commands in linux: +% cd /tmp +% epspdf fig1.eps +% epspdf fig2.eps +% epspdf fig3.eps +% and copy the fig1.pdf, etc., files to your latex folder. diff --git a/OU3/rapport_ou3 (conflicted copy 2025-05-09 092332).pdf b/OU3/rapport_ou3 (conflicted copy 2025-05-09 092332).pdf new file mode 100644 index 0000000..9d68670 Binary files /dev/null and b/OU3/rapport_ou3 (conflicted copy 2025-05-09 092332).pdf differ diff --git a/OU3/rapport_ou3.pdf b/OU3/rapport_ou3.pdf new file mode 100644 index 0000000..ddfae74 Binary files /dev/null and b/OU3/rapport_ou3.pdf differ diff --git a/OU3/src/Makefile b/OU3/src/Makefile new file mode 100644 index 0000000..e3d0733 --- /dev/null +++ b/OU3/src/Makefile @@ -0,0 +1,13 @@ +# List of directories containing sub-projects +SUB_DIRS := array_1d array_2d dlist int_array_1d int_list int_list_array int_stack list int_queue queue stack table + +# Rule to call 'make all' in each sub-directory +all: + for dir in $(SUB_DIRS); do \ + $(MAKE) -C $$dir all; \ + done + +clean: + for dir in $(SUB_DIRS); do \ + $(MAKE) -C $$dir clean; \ + done diff --git a/OU3/src/array_1d/Makefile b/OU3/src/array_1d/Makefile new file mode 100644 index 0000000..ad8fc86 --- /dev/null +++ b/OU3/src/array_1d/Makefile @@ -0,0 +1,49 @@ +MWE = array_1d_mwe1 array_1d_mwe2 array_1d_mwe3 array_1d_mwe4 array_1d_mwe1i array_1d_mwe2i + +SRC = array_1d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +array_1d_mwe1: array_1d_mwe1.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe2: array_1d_mwe2.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe3: array_1d_mwe3.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe4: array_1d_mwe4.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe1i: array_1d_mwe1i.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe2i: array_1d_mwe2i.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: array_1d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: array_1d_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: array_1d_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: array_1d_mwe4 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/array_1d/array_1d.c b/OU3/src/array_1d/array_1d.c new file mode 100644 index 0000000..6244fac --- /dev/null +++ b/OU3/src/array_1d/array_1d.c @@ -0,0 +1,491 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-05-10: Added print_internal. + */ + +// =========== INTERNAL DATA TYPES ============ + +struct array_1d { + int low; // Low index limit. + int high; // High index limit. + int array_size; // Number of array elements. + void **values; // Pointer to where the actual values are stored. + kill_function kill_func; +}; + +// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_1d *array_1d_create(int lo, int hi, kill_function kill_func) +{ + // Allocate array structure. + array_1d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low = lo; + a->high = hi; + + // Number of elements. + a->array_size = hi - lo + 1; + + // Store kill function. + a->kill_func = kill_func; + + a->values = calloc(a->array_size, sizeof(void *)); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int array_1d_low(const array_1d *a) +{ + return a->low; +} + +/** + * array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int array_1d_high(const array_1d *a) +{ + return a->high; +} + +/** + * array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_1d_inspect_value(const array_1d *a, int i) +{ + int offset = i - array_1d_low(a); + // Return the value. + return a->values[offset]; +} + +/** + * array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_1d_has_value(const array_1d *a, int i) +{ + int offset = i - array_1d_low(a); + // Return true if the value is not NULL. + return a->values[offset] != NULL; +} + +/** + * array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_1d_set_value(array_1d *a, void *v, int i) +{ + int offset = i - array_1d_low(a); + // Call kill_func if specified and old element value was non-NULL. + if (a->kill_func != NULL && a->values[offset] != NULL) { + a->kill_func( a->values[offset] ); + } + // Set value. + a->values[offset] = v; +} + +/** + * array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_1d_kill(array_1d *a) +{ + if (a->kill_func != NULL) { + // Return user-allocated memory for each non-NULL element. + for (int i = 0; i < a->array_size; i++) { + if (a->values[i] != NULL) { + a->kill_func(a->values[i]); + } + } + } + // Return memory for value pointers. + free(a->values); + // Return memory for array structure. + free(a); +} + +/** + * array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_1d_print(const array_1d *a, inspect_callback print_func) +{ + printf("[ "); + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + printf("["); + print_func(array_1d_inspect_value(a, i)); + printf("]"); + } else { + printf(" []"); + } + if (i < array_1d_high(a)) { + printf(", "); + } + } + printf(" ]\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY array_1d_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|low\\n%d|high\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(a), PTR2ADDR(a->kill_func), a->low, a->high, a->array_size, + PTR2ADDR(a->values)); +} + +void print_values(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int i=0; i < a->array_size; i++) { + printf("<%02d>%d\\n%02d\\n%04lx", i, i + a->low, i, PTR2ADDR(a->values[i])); + if (i < a->array_size - 1) { + printf("|"); + } + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a)); + if (a->values == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(a->values)); + } + printf(" [label=\"values\"]\n"); +} + +// Print nodes for each value memory block +static void print_value_nodes(int indent_level, const array_1d *a, inspect_callback print_func) +{ + for (int i=0; i <= a->array_size; i++) { + if (a->values[i] != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i])); + if (print_func != NULL) { + print_func(a->values[i]); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i])); + } + } +} + +// Print edges from each value pointer to payload memory +static void print_value_edges(int indent_level, const array_1d *a) +{ + for (int i=0; i < a->array_size; i++) { + // Buffer to store port name in. Good for array up to 1e9 elements. + char port[15]; + // Create port name + sprintf(port, "%02d", i); + if (a->kill_func) { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red"); + } else { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red style=dashed"); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * array_1d_print_internal() - Print the internal structure of the array in dot format. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_1D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_array_1d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array + print_values(il, a); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Print nodes for each value memory block + print_value_nodes(il, a, print_func); + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, a); + + // Print edges from each value pointer to payload memory + print_value_edges(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/array_1d/array_1d_mwe1.c b/OU3/src/array_1d/array_1d_mwe1.c new file mode 100644 index 0000000..5664909 --- /dev/null +++ b/OU3/src/array_1d/array_1d_mwe1.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * Removed use of automatic, magic, free_handler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + * v1.31 2024-04-05: Bugfix: Remove call to array_1d_print_internal + * that shouldn't be here. + */ + +#define VERSION "v1.31" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, NULL); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a, v, i); + } + printf("Array after inserting i^2, i=1,...,6:\n"); + array_1d_print(a, print_int); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + int *v = array_1d_inspect_value(a, i); + int_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_1d/array_1d_mwe1i.c b/OU3/src/array_1d/array_1d_mwe1i.c new file mode 100644 index 0000000..fba94d1 --- /dev/null +++ b/OU3/src/array_1d/array_1d_mwe1i.c @@ -0,0 +1,146 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-05: Adapted from array_1d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_1d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, NULL); + + printf("Empty array from the outside:\n"); + array_1d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + printf("Array from the outside after setting half the values:\n"); + array_1d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting 3 values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_1d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_1d_high(a) ; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + + printf("Array from the outside after setting all the values:\n"); + array_1d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all 6 values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_1d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + int *v=array_1d_inspect_value(a,i); + int_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_1d/array_1d_mwe2.c b/OU3/src/array_1d/array_1d_mwe2.c new file mode 100644 index 0000000..04b002f --- /dev/null +++ b/OU3/src/array_1d/array_1d_mwe2.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 2 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, int_kill); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a, v, i); + } + printf("Array after inserting i^2, i=1,...,6:\n"); + array_1d_print(a, print_int); + + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_1d/array_1d_mwe2i.c b/OU3/src/array_1d/array_1d_mwe2i.c new file mode 100644 index 0000000..9462bb8 --- /dev/null +++ b/OU3/src/array_1d/array_1d_mwe2i.c @@ -0,0 +1,140 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-05: Adapted from array_1d_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_1d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, int_kill); + + printf("Empty array from the outside:\n"); + array_1d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + printf("Array from the outside after setting half the values:\n"); + array_1d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting 3 values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_1d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_1d_high(a) ; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + + printf("Array from the outside after setting all the values:\n"); + array_1d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all 6 values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_1d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Clean up the array. + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_1d/array_1d_mwe3.c b/OU3/src/array_1d/array_1d_mwe3.c new file mode 100644 index 0000000..318de8f --- /dev/null +++ b/OU3/src/array_1d/array_1d_mwe3.c @@ -0,0 +1,99 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 3 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * Removed use of automatic, magic, free_handler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Struct with month name and number of days. +typedef struct month { + char *name; + int days; +} month; + +// Months are stored via void pointers. Convert the given pointer and +// print the dereferenced values. +void print_month(const void *data) +{ + // Convert void pointer to pointer to month. + const month *m=data; + printf("(%s, %d)", m->name, m->days); +} + +// Allocate space for and populate a month structure +month *month_create(const char *name, int days) +{ + // Allocate memory for a month structure. + month *m = malloc(sizeof(*m)); + // Allocate memory for the month name. + m->name = calloc(strlen(name) + 1, sizeof(char)); + // Copy the string. + strcpy(m->name, name); + // Set days. + m->days = days; + return m; +} + +// Function to free both month structure and char * with name. +void month_kill(void *data) +{ + // Convert void pointer to pointer to month. + month *m=data; + free(m->name); + free(m); +} + +int main(void) +{ + printf("%s, %s %s: Create month array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + const char *month_names[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int month_days[12] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + // Create an array with 12 positions. + array_1d *a = array_1d_create(1, 12, NULL); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate and populate a month structure. + month *m=month_create(month_names[i - 1], month_days[i - 1]); + // Set value in array. + array_1d_set_value(a,m,i); + } + printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n"); + array_1d_print(a, print_month); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a,i)) { + month *v=array_1d_inspect_value(a,i); + month_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_1d/array_1d_mwe4.c b/OU3/src/array_1d/array_1d_mwe4.c new file mode 100644 index 0000000..24ed388 --- /dev/null +++ b/OU3/src/array_1d/array_1d_mwe4.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 4 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Struct with month name and number of days. +typedef struct month { + char *name; + int days; +} month; + +// Months are stored via void pointers. Convert the given pointer and +// print the dereferenced values. +void print_month(const void *data) +{ + // Convert void pointer to pointer to month. + const month *m=data; + printf("(%s, %d)", m->name, m->days); +} + +// Allocate space for and populate a month structure +month *month_create(const char *name, int days) +{ + // Allocate memory for a month structure. + month *m = malloc(sizeof(*m)); + // Allocate memory for the month name. + m->name = calloc(strlen(name) + 1, sizeof(char)); + // Copy the string. + strcpy(m->name, name); + // Set days. + m->days = days; + return m; +} + +// Function to free both month structure and char * with name. +void month_kill(void *data) +{ + // Convert void pointer to pointer to month. + month *m=data; + free(m->name); + free(m); +} + +int main(void) +{ + printf("%s, %s %s: Create month array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + const char *month_names[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int month_days[12] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + // Create an array with 12 positions. + array_1d *a = array_1d_create(1, 12, month_kill); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate and populate a month structure. + month *m=month_create(month_names[i - 1], month_days[i - 1]); + // Set value in array. + array_1d_set_value(a,m,i); + } + printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n"); + array_1d_print(a, print_month); + + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_2d/Makefile b/OU3/src/array_2d/Makefile new file mode 100644 index 0000000..e64cc6a --- /dev/null +++ b/OU3/src/array_2d/Makefile @@ -0,0 +1,37 @@ +MWE = array_2d_mwe1 array_2d_mwe2 array_2d_mwe1i array_2d_mwe2i + +SRC = array_2d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +array_2d_mwe1: array_2d_mwe1.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe2: array_2d_mwe2.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe1i: array_2d_mwe1i.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe2i: array_2d_mwe2i.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: array_2d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: array_2d_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/array_2d/array_2d.c b/OU3/src/array_2d/array_2d.c new file mode 100644 index 0000000..2c10938 --- /dev/null +++ b/OU3/src/array_2d/array_2d.c @@ -0,0 +1,526 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 2D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Moved freehandler to last in create parameter list. + * v1.2 2024-05-10: Added print_internal. + * v1.21 2024-05-15: Bugfix to get rid of sprintf warning. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct array_2d { + int low[2]; // Low index limits. + int high[2]; // High index limits. + int array_size; // Number of array elements. + void **values; // Pointer to where the actual values are stored. + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * array_2d_create() - Create an array without values. + * @lo1: low index limit for first dimension. + * @hi1: high index limit for first dimension. + * @lo2: low index limit for second dimension. + * @hi2: high index limit for second dimension. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2, + kill_function kill_func) +{ + // Allocate array structure. + array_2d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low[0] = lo1; + a->low[1] = lo2; + a->high[0] = hi1; + a->high[1] = hi2; + + // Number of elements. + a->array_size = (hi1 - lo1 + 1) * (hi2 - lo2 + 1); + + // Store kill function. + a->kill_func = kill_func; + + a->values=calloc(a->array_size, sizeof(void *)); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * array_2d_low() - Return the low index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The low index limit for dimension number d. + */ +int array_2d_low(const array_2d *a, int d) +{ + return a->low[d - 1]; +} + +/** + * array_2d_high() - Return the high index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The high index limit for dimension number d. + */ +int array_2d_high(const array_2d *a, int d) +{ + return a->high[d - 1]; +} + +/** + * array_2d_linear_index() - Internal function to compute linear index from list + * of indices. + * @a: array to inspect. + * @i: First index. + * @j: Second index. + * + * Returns: The linear index corresponding to the list of indices. + * NOTE: The result is undefined if the number of arguments + * or index value are out of bounds. + */ +static int array_2d_linear_index(const array_2d *a, int i, int j) +{ + int rows = a->high[0] - a->low[0] + 1; + int ix=(i - a->low[0]) + (j - a->low[1]) * rows; + return ix; +} + +/** + * array_2d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: First index of position to inspect. + * @j: Second index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_2d_inspect_value(const array_2d *a, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Return the value. + return a->values[ix]; +} + +/** + * array_2d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: First index of position to inspect. + * @j: Second index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_2d_has_value(const array_2d *a, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Return true if the value is not NULL. + return a->values[ix] != NULL; +} + +/** + * array_2d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: First index of position to modify. + * @j: Second index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_2d_set_value(array_2d *a, void *v, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Call kill_func if specified and old element value was non-NULL. + if (a->kill_func != NULL && a->values[ix] != NULL) { + a->kill_func(a->values[ix]); + } + // Set value. + a->values[ix]=v; +} + +/** + * array_2d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_2d_kill(array_2d *a) +{ + if (a->kill_func) { + // Return user-allocated memory for each non-NULL element. + for (int i=0; iarray_size; i++) { + if (a->values[i] != NULL) { + a->kill_func(a->values[i]); + } + } + } + // Free actual storage. + free(a->values); + // Free array structure. + free(a); +} + +/** + * array_2d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_2d_print(const array_2d *a, inspect_callback print_func) +{ + printf("[\n"); + for (int i=array_2d_low(a,1); i<=array_2d_high(a,1); i++) { + printf(" [ "); + for (int j=array_2d_low(a,2); j<=array_2d_high(a,2); j++) { + if (array_2d_has_value(a,i,j)) { + printf("["); + print_func(array_2d_inspect_value(a,i,j)); + printf("]"); + } else { + printf("[ ]"); + } + if (j ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const array_2d *p) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"func\\n%04lx|low[0]\\n%d|high[0]\\n%d" + "|low[1]\\n%d|high[1]\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->kill_func), p->low[0], p->high[0], + p->low[1], p->high[1], p->array_size, PTR2ADDR(p->values)); +} + +static void print_values(int indent_level, const array_2d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int j=array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + for (int i=array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + int li=array_2d_linear_index(a, i, j); + printf("<%02d>(%d,%d)\\n%02d\\n%04lx", + li, i, j, li, PTR2ADDR(a->values[li])); + if (li < a->array_size - 1) { + printf("|"); + } + } + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const array_2d *a) +{ + iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a)); + if (a->values == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(a->values)); + } + printf(" [label=\"values\"]\n"); +} + +// Print nodes for each value memory block +static void print_value_nodes(int indent_level, const array_2d *a, inspect_callback print_func) +{ + for (int i=0; i <= a->array_size; i++) { + if (a->values[i] != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i])); + if (print_func != NULL) { + print_func(a->values[i]); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i])); + } + } +} + +// Print edges from each value pointer to payload memory +static void print_value_edges(int indent_level, const array_2d *a) +{ + for (int i=0; i < a->array_size; i++) { + // Buffer to store port name in. Good for array up to 1e9 elements. + char port[15]; + // Create port name + sprintf(port, "%02d", i); + if (a->kill_func) { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red"); + } else { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red style=dashed"); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * array_2d_print_internal() - Print the arrays internal structure. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_2D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_array_2d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array + print_values(il, a); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Print nodes for each value memory block + print_value_nodes(il, a, print_func); + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, a); + + // Print edges from each value pointer to payload memory + print_value_edges(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/array_2d/array_2d_mwe1.c b/OU3/src/array_2d/array_2d_mwe1.c new file mode 100644 index 0000000..4261433 --- /dev/null +++ b/OU3/src/array_2d/array_2d_mwe1.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2023-01-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, NULL); + + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("After filling the array with values:\n"); + array_2d_print(a, print_int); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_2d/array_2d_mwe1i.c b/OU3/src/array_2d/array_2d_mwe1i.c new file mode 100644 index 0000000..6a600e7 --- /dev/null +++ b/OU3/src/array_2d/array_2d_mwe1i.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-07: Adapted from array_2d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-07" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_2d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_2d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, NULL); + + printf("Empty array from the outside:\n"); + array_2d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("Array from the outside after setting half the values:\n"); + array_2d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting half of the values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_2d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + + printf("Array from the outside after setting all the values:\n"); + array_2d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all the values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_2d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_2d/array_2d_mwe2.c b/OU3/src/array_2d/array_2d_mwe2.c new file mode 100644 index 0000000..bc48a0f --- /dev/null +++ b/OU3/src/array_2d/array_2d_mwe2.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2023-01-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, int_kill); + + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("After filling the array with values:\n"); + array_2d_print(a, print_int); + + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/array_2d/array_2d_mwe2i.c b/OU3/src/array_2d/array_2d_mwe2i.c new file mode 100644 index 0000000..ecb1167 --- /dev/null +++ b/OU3/src/array_2d/array_2d_mwe2i.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-07: Adapted from array_2d_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-07" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_2d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_2d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, int_kill); + + printf("Empty array from the outside:\n"); + array_2d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("Array from the outside after setting half the values:\n"); + array_2d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting half of the values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_2d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + + printf("Array from the outside after setting all the values:\n"); + array_2d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all the values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_2d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/dlist/Makefile b/OU3/src/dlist/Makefile new file mode 100644 index 0000000..914da3e --- /dev/null +++ b/OU3/src/dlist/Makefile @@ -0,0 +1,42 @@ +MWE = dlist_mwe1 dlist_mwe2 dlist_mwe1i dlist_mwe2i + +SRC = dlist.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +clean: + -rm -f $(MWE) $(OBJ) + +dlist_mwe1: dlist_mwe1.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe2: dlist_mwe2.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe1i: dlist_mwe1i.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe2i: dlist_mwe2i.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: dlist_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: dlist_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: dlist_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: dlist_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/dlist/README.md b/OU3/src/dlist/README.md new file mode 100644 index 0000000..85f7b69 --- /dev/null +++ b/OU3/src/dlist/README.md @@ -0,0 +1,9 @@ +# Riktad lista + +En implementation av ADTn _Riktad Lista_ baserad på en enkel-länkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + diff --git a/OU3/src/dlist/dlist.c b/OU3/src/dlist/dlist.c new file mode 100644 index 0000000..414c8dc --- /dev/null +++ b/OU3/src/dlist/dlist.c @@ -0,0 +1,588 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic, undirected list for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added dlist_pos_are_equal and dlist_pos_is_valid functions. + * v1.2 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list elements are implemented as one-cells with a forward link. + * The list position is a pointer to the internal cell before the cell + * with the value. + */ +typedef struct cell { + struct cell *next; + void *val; +} cell; + + +struct dlist { + cell *head; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * dlist_empty() - Create an empty dlist. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +dlist *dlist_empty(kill_function kill_func) +{ + // Allocate memory for the list structure. + dlist *l = calloc(1, sizeof(*l)); + + // Allocate memory for the list head. + l->head = calloc(1, sizeof(cell)); + + // No elements in list so far. + l->head->next = NULL; + + // Store the kill function. + l->kill_func = kill_func; + + return l; +} + +/** + * dlist_is_empty() - Check if a dlist is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool dlist_is_empty(const dlist *l) +{ + return (l->head->next == NULL); +} + +/** + * dlist_first() - Return the first position of a dlist, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +dlist_pos dlist_first(const dlist *l) +{ + return l->head; +} + +/** + * dlist_next() - Return the next position in a dlist. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +dlist_pos dlist_next(const dlist *l, const dlist_pos p) +{ + if (dlist_is_end(l, p)) { + // This should really throw an error. + fprintf(stderr,"dlist_next: Warning: Trying to navigate " + "past end of list!"); + } + return p->next; +} + +/** + * dlist_is_end() - Check if a given position is at the end of a dlist. + * @l: List to inspect. + * @p: Any valid position in the list. + * + * Returns: True if p is at the end of the list. + */ +bool dlist_is_end(const dlist *l, const dlist_pos p) +{ + return p->next == NULL; +} + +/** + * dlist_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *dlist_inspect(const dlist *l, const dlist_pos p) +{ + if (dlist_is_end(l, p)) { + // This should really throw an error. + fprintf(stderr,"dlist_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->next->val; +} + +/** + * dlist_insert() - Insert a new element with a given value into a dlist. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p) +{ + // Create new element. + dlist_pos new_pos=calloc(1, sizeof(cell)); + // Set value. + new_pos->val=v; + + // Set links. + new_pos->next=p->next; + p->next=new_pos; + + return p; +} + +/** + * dlist_remove() - Remove an element from a dlist. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +dlist_pos dlist_remove(dlist *l, const dlist_pos p) +{ + // Cell to remove. + dlist_pos c=p->next; + + // Link past cell to remove. + p->next=c->next; + + // Call kill_func if registered. + if(l->kill_func != NULL) { + // Return any user-allocated memory for the value. + l->kill_func(c->val); + } + // Free the memory allocated to the cell itself. + free(c); + // Return the position of the next element. + return p; +} + +/** + * dlist_kill() - Destroy a given dlist. + * @l: List to destroy. + * + * Return all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void dlist_kill(dlist *l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + dlist_pos p = dlist_first(l); + + // Remove first element until list is empty. + while(!dlist_is_empty(l)) { + p = dlist_remove(l, p); + } + + // Free the head and the list itself. + free(l->head); + free(l); +} + +/** + * dlist_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void dlist_print(const dlist *l, inspect_callback print_func) +{ + // Start at the beginning of the list. + dlist_pos p = dlist_first(l); + + printf("( "); + while (!dlist_is_end(l, p)) { + // Call print_func with the element value at the + // current position. + print_func(dlist_inspect(l, p)); + // Advance to next position. + p = dlist_next(l, p); + // Print separator unless at element. + if (!dlist_is_end(l, p)) { + printf(", "); + } + } + printf(" )\n"); +} + +/** + * dlist_pos_is_equal() - Return true if two positions in a dlist are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * dlist_pos_is_valid() - Return true for a valid position in a dlist. + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool dlist_pos_is_valid(const dlist *l, const dlist_pos p) +{ + // Iterate over all positions in l. + dlist_pos q = dlist_first(l); + while (!dlist_is_end(l, q)) { + if (dlist_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = dlist_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the dlist head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const dlist *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|head\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l)); +} + +// Print edges from the list head to the head cell. +static void print_head_edges(int indent_level, const dlist *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const dlist_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%04lx|next\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const dlist *l, const dlist_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + + // Ignore val ptr for head and tail nodes. + if (p == l->head) { + return; + } + if (l->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * dlist_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph DLIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_dlist_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_dlist_%d { label=\"DList\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + dlist_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = l->head; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/dlist/dlist_mwe1.c b/OU3/src/dlist/dlist_mwe1.c new file mode 100644 index 0000000..2f2d14b --- /dev/null +++ b/OU3/src/dlist/dlist_mwe1.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for dlist.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit copy/kill string functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// Test program. +int main(void) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(NULL); + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList after inserting 6 strings:\n"); + dlist_print(l, print_string); + + // Traverse the list and deallocate each value. + p = dlist_first(l); + while (!dlist_is_end(l,p)) { + char *v=dlist_inspect(l,p); + kill_string(v); + p=dlist_remove(l,p); + } + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/dlist/dlist_mwe1i.c b/OU3/src/dlist/dlist_mwe1i.c new file mode 100644 index 0000000..fd9ff6d --- /dev/null +++ b/OU3/src/dlist/dlist_mwe1i.c @@ -0,0 +1,142 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for dlist.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "borrows" the payload memory, i.e., the user of the list is + * responsible for deallocating the payload memory. See dlist_mwe2i.c + * for a version where the list takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +void print_string_internal(const void *value) +{ + const char *s=value; + // We must double-escape the double quotes since the output will + // be parsed a second time by graphviz. + printf("\\\"%s\\\"", s); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const dlist *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + dlist_print_internal(l, print_string_internal, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +// Test program. +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(NULL); + + printf("Empty list from the outside:\n"); + dlist_print(l, print_string); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList from the outside after inserting 6 strings:\n"); + dlist_print(l, print_string); + const char *long_desc = __FILE__ + ": Internal structure of the DList after inserting 6 strings.\n" + "Red lines are used for the list payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the list, i.e., the payload\n" + "memory will NOT be deallocated by the list.\n\n" + "See dlist_mwe2i for a list example\nthat owns the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Traverse the list and deallocate each value. + p = dlist_first(l); + while (!dlist_is_end(l,p)) { + char *v=dlist_inspect(l,p); + kill_string(v); + p=dlist_remove(l,p); + } + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/dlist/dlist_mwe2.c b/OU3/src/dlist/dlist_mwe2.c new file mode 100644 index 0000000..0db799a --- /dev/null +++ b/OU3/src/dlist/dlist_mwe2.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 2 for dlist.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit copy/kill string functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// Test program. +int main(void) +{ + printf("%s, %s %s: Create list of strings with standard memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(kill_string); + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList after inserting 6 strings:\n"); + dlist_print(l, print_string); + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/dlist/dlist_mwe2i.c b/OU3/src/dlist/dlist_mwe2i.c new file mode 100644 index 0000000..ceef4fc --- /dev/null +++ b/OU3/src/dlist/dlist_mwe2i.c @@ -0,0 +1,136 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for dlist.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "owns" the payload memory, i.e., the list takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See list_mwe1i.c for a version + * where the list does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +void print_string_internal(const void *value) +{ + const char *s=value; + // We must double-escape the double quotes since the output will + // be parsed a second time by graphviz. + printf("\\\"%s\\\"", s); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const dlist *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + dlist_print_internal(l, print_string_internal, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +// Test program. +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(kill_string); + + printf("Empty list from the outside:\n"); + dlist_print(l, print_string); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList from the outside after inserting 6 strings:\n"); + dlist_print(l, print_string); + const char *long_desc = __FILE__ + ": Internal structure of the DList after inserting 6 strings.\n" + "Red lines are used for the list payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the list, i.e., the payload\n" + "memory WILL be deallocated by the list.\n\n" + "See dlist_mwe1i for a list example\nthat borrows the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_array_1d/Makefile b/OU3/src/int_array_1d/Makefile new file mode 100644 index 0000000..a0491dc --- /dev/null +++ b/OU3/src/int_array_1d/Makefile @@ -0,0 +1,28 @@ +MWE = int_array_1d_mwe1 int_array_1d_mwe1i + +SRC = int_array_1d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_array_1d_mwe1: int_array_1d_mwe1.c int_array_1d.c + gcc -o $@ $(CFLAGS) $^ + +int_array_1d_mwe1i: int_array_1d_mwe1i.c int_array_1d.c + gcc -o $@ $(CFLAGS) $^ + +memtest: int_array_1d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/int_array_1d/int_array_1d.c b/OU3/src/int_array_1d/int_array_1d.c new file mode 100644 index 0000000..9c606fe --- /dev/null +++ b/OU3/src/int_array_1d/int_array_1d.c @@ -0,0 +1,424 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added print_internal. + */ + +// =========== INTERNAL DATA TYPES ============ + +struct int_array_1d { + int low; // Low index limit. + int high; // High index limit. + int array_size; // Number of array elements. + int *values; // Pointer to where the actual values are stored. +}; + +// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * int_array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +int_array_1d *int_array_1d_create(int lo, int hi) +{ + // Allocate array structure. + int_array_1d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low = lo; + a->high = hi; + + // Number of elements. + a->array_size = hi - lo + 1; + + a->values=calloc(a->array_size, sizeof(a->values[0])); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * int_array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int int_array_1d_low(const int_array_1d *a) +{ + return a->low; +} + +/** + * int_array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int int_array_1d_high(const int_array_1d *a) +{ + return a->high; +} + +/** + * int_array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +int int_array_1d_inspect_value(const int_array_1d *a, int i) +{ + int offset=i - int_array_1d_low(a); + // Return the value. + return a->values[offset]; +} + +/** + * int_array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool int_array_1d_has_value(const int_array_1d *a, int i) +{ + int offset=i - int_array_1d_low(a); + // Return true if the value is not 0. + return a->values[offset] != 0; +} + +/** + * int_array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or 0 to clear value. + * @i: index of position to modify. + * + * Returns: Nothing. + */ +void int_array_1d_set_value(int_array_1d *a, int v, int i) +{ + int offset=i - int_array_1d_low(a); + // Set value. + a->values[offset] = v; +} + +/** + * int_array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Returns: Nothing. + */ +void int_array_1d_kill(int_array_1d *a) +{ + // Free actual storage. + free(a->values); + // Free array structure. + free(a); +} + +/** + * int_array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void int_array_1d_print(const int_array_1d *a) +{ + printf("[ "); + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + if (int_array_1d_has_value(a, i)) { + printf("[%d]", int_array_1d_inspect_value(a, i)); + } else { + printf(" []"); + } + if (i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const int_array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"low\\n%d|high\\n%d" + "|array_size\\n%d|values\\n%04lx\"]\n", PTR2ADDR(a), + a->low, a->high, a->array_size, PTR2ADDR(a->values)); +} + +/** + * print_values() - Print a node containing all positions in the array. + * @indent_level: Indentation level. + * @a: Array to inspect. + * @max_values_to_print: Maximum number of values to output. + * + * Will print dot code to display each value in the array, up to + * max_values_to_print. If there are more values to print, adds an + * ellipsis (...) at the end. + * + * Returns: Nothing. + */ +void print_values(int indent_level, const int_array_1d *a, int max_values_to_print) +{ + int values_to_print = a->array_size; + int truncated = false; + if (values_to_print > max_values_to_print) { + values_to_print = max_values_to_print; + truncated = true; + } + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int i=0; i < values_to_print; i++) { + printf("%d\\n%02d\\n%d", i + a->low, i, a->values[i]); + if (i < values_to_print - 1) { + printf("|"); + } + } + if (truncated) { + // Add ellipsis + printf("|\\n...\\n"); + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const int_array_1d *a) +{ + print_edge(indent_level, a, a->values, "v", "values", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * int_array_1d_print_internal() - Print the arrays internal structure in dot format. + * @a: Array to inspect. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_1D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_int_array_1d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_int_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array. Limit output to 20 elements. + print_values(il, a, 20); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/int_array_1d/int_array_1d_mwe1.c b/OU3/src/int_array_1d/int_array_1d_mwe1.c new file mode 100644 index 0000000..8f37f94 --- /dev/null +++ b/OU3/src/int_array_1d/int_array_1d_mwe1.c @@ -0,0 +1,38 @@ +#include +#include + +/* + * Minimum working example for int_array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-01-14" + +int main(void) +{ + printf("%s, %s %s: Create typed integer array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + int_array_1d *a = int_array_1d_create(1, 6); + + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + // Store square of index. + int_array_1d_set_value(a, i*i, i); + // Print array after setting each value. + printf("After setting value at index %d:\n", i); + int_array_1d_print(a); + } + + int_array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_array_1d/int_array_1d_mwe1i.c b/OU3/src/int_array_1d/int_array_1d_mwe1i.c new file mode 100644 index 0000000..44c0f9a --- /dev/null +++ b/OU3/src/int_array_1d/int_array_1d_mwe1i.c @@ -0,0 +1,83 @@ +#include +#include + +/* + * Minimum working example for int_array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-08: Adapted from int_array_1d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-08" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const int_array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + int_array_1d_print_internal(a, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + int_array_1d *a = int_array_1d_create(1, 6); + + printf("Empty array from the outside:\n"); + int_array_1d_print(a); + print_internal_with_cut_lines(a, "Empty array"); + + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + // Store square of index. + int_array_1d_set_value(a, i*i, i); + } + // Print array after setting all values. + printf("Full array from the outside:\n"); + int_array_1d_print(a); + print_internal_with_cut_lines(a, "Full array from the inside"); + + int_array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_list/Makefile b/OU3/src/int_list/Makefile new file mode 100644 index 0000000..75c4e20 --- /dev/null +++ b/OU3/src/int_list/Makefile @@ -0,0 +1,44 @@ +MWE = int_list_mwe1 int_list_mwe1i +TEST = int_list_test + +SRC = int_list.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Test programs. +test: $(TEST) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +int_list_mwe1: int_list_mwe1.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +int_list_mwe1i: int_list_mwe1i.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +int_list_test: int_list_test.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +test_run: test + # Run the test + ./$(TEST) + +memtest_mwe: int_list_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test: int_list_test + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest: memtest_mwe memtest_test diff --git a/OU3/src/int_list/int_list.c b/OU3/src/int_list/int_list.c new file mode 100644 index 0000000..3d4b645 --- /dev/null +++ b/OU3/src/int_list/int_list.c @@ -0,0 +1,542 @@ +#include +#include +#include + +#include + +/* + * Implementation of a typed, undirected list of integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses linked + * 2-cells. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.01 2018-03-26: Bugfix: Corrected const declaration in remove. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v2.0 2024-05-10: Added print_internal. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list elements are implemented as two-cells with forward and + * backward links and place to store one integer. The list uses two + * border cells at the start and end of the list. + */ +typedef struct cell { + struct cell *next; + struct cell *prev; + int val; +} cell; + +struct list { + cell *head; + cell *tail; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void) +{ + // Allocate memory for the list head. + list *l = calloc(1, sizeof(list)); + + // Allocate memory for the border cells. + l->head = calloc(1, sizeof(cell)); + l->tail = calloc(1, sizeof(cell)); + + // Set consistent links between border elements. + l->head->next = l->tail; + l->tail->prev = l->head; + + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list * l) +{ + // List is empty if there are no cells between head and tail. + return (l->head->next == l->tail); +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list * l) +{ + // First position is position of first element. + return l->head->next; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list * l) +{ + // Last position is position *after* last element. + return l->tail; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); + } + return p->next; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p->prev; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->val; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list * l, int v, const list_pos p) +{ + // Allocate memory for a new cell. + list_pos e = malloc(sizeof(cell)); + + // Store the value. + e->val = v; + // Add links to/from the new cell. + e->next = p; + e->prev = p->prev; + p->prev = e; + e->prev->next = e; + + // Return the position of the new cell. + return e; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Remember return position. + list_pos next_pos = p->next; + // Link past this element. + p->prev->next = p->next; + p->next->prev = p->prev; + + // Free the memory allocated to the cell itself. + free(p); + // Return the position of the next element. + return next_pos; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. + * + * Returns: Nothing. + */ +void list_kill(list * l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + list_pos p = list_first(l); + + // Remove first element until list is empty. + while (!list_is_empty(l)) { + p = list_remove(l, p); + } + + // Free border elements and the list head. + free(l->head); + free(l->tail); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * + * Iterates over the list and print each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list * l) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + printf("[%d]\n", list_inspect(l, p)); + p = list_next(l, p); + } +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // Iterate over all positions in l. + list_pos q = list_first(l); + while (!list_pos_is_equal(l, q, list_end(l))) { + if (list_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = list_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY int_list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->head), PTR2ADDR(l->tail), PTR2ADDR(l)); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edges(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); + print_edge(indent_level, l, l->tail, "t", "tail", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const list_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%d|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), p->val, PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const list *l, const list_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + print_edge(indent_level, p, p->prev, "p", "prev", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + list_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/int_list/int_list_mwe1.c b/OU3/src/int_list/int_list_mwe1.c new file mode 100644 index 0000000..0847169 --- /dev/null +++ b/OU3/src/int_list/int_list_mwe1.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l); + + // Insert the value 2 at the second position in the list. + list_insert(l, 2, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_list/int_list_mwe1i.c b/OU3/src/int_list/int_list_mwe1i.c new file mode 100644 index 0000000..4b67328 --- /dev/null +++ b/OU3/src/int_list/int_list_mwe1i.c @@ -0,0 +1,97 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + printf("Empty list from the outside.\n"); + list_print(l); + print_internal_with_cut_lines(l, "Empty list showing the internals"); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List from the outside after inserting one value:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting one value showing the internals"); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List from the outside after inserting second value at the end:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals"); + + printf("List after removing first element:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after removing first element showing the internals"); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_list/int_list_test.c b/OU3/src/int_list/int_list_test.c new file mode 100644 index 0000000..2ffde69 --- /dev/null +++ b/OU3/src/int_list/int_list_test.c @@ -0,0 +1,1101 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create an empty list + list *l = list_empty(); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create an empty list + list *l = list_empty(); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create an empty list + list *l = list_empty(); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(); + // Insert an element at the only position in the empty list + list_insert(l, v, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(); + // Insert the first element + list_pos p = list_insert(l, v1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, v2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, 24, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int read_value = list_inspect(l, list_first(l)); + + if (!value_equal(read_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", read_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Insert new element at end + list_insert(l, v[1], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Insert new element at end + list_insert(l, v[2], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Insert new element at next(first()) + list_insert(l, v[1], list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the end of the list + list_insert(l, n, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (!value_equal(i + 1, list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the beginning of the list + list_insert(l, n, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (!value_equal(n - i, list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_list datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU3/src/int_list_array/Makefile b/OU3/src/int_list_array/Makefile new file mode 100644 index 0000000..9cfd2b0 --- /dev/null +++ b/OU3/src/int_list_array/Makefile @@ -0,0 +1,44 @@ +MWE = int_list_array_mwe1 int_list_array_mwe1i +TEST = int_list_array_test + +SRC = int_list_array.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Test programs. +test: $(TEST) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +int_list_array_mwe1: int_list_array_mwe1.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +int_list_array_mwe1i: int_list_array_mwe1i.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +int_list_array_test: int_list_array_test.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +test_run: test + # Run the test + ./$(TEST) + +memtest_mwe1: int_list_array_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test: int_list_array_test + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest: memtest_mwe1 memtest_test diff --git a/OU3/src/int_list_array/int_list_array.c b/OU3/src/int_list_array/int_list_array.c new file mode 100644 index 0000000..a54e1b5 --- /dev/null +++ b/OU3/src/int_list_array/int_list_array.c @@ -0,0 +1,515 @@ +#include +#include +#include // For bcopy +#include + +#include + +/* + * Implementation of a typed, undirected list of integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * static array. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. Bugfix + * in list_remove. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list is implemented as a static array. + */ +struct list { + int last_used_pos; + int *values; + int array_size; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +#define ARRAY_MAX_SIZE 100 + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void) +{ + // Allocate memory for the list head. + list *l=malloc(sizeof(list)); + // Set the size + l->array_size = ARRAY_MAX_SIZE; + // Allocate memory for the elements. + l->values=calloc(l->array_size,sizeof(l->values[0])); + // Set last used position. + l->last_used_pos=-1; + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list * l) +{ + // List is empty if no elements are used. + return l->last_used_pos < 0; +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list * l) +{ + // First position is always 0. + return 0; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list * l) +{ + // Last position is position *after* last used element. + return l->last_used_pos + 1; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate " + "past end of list!"); + } + return p + 1; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p - 1; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return l->values[p]; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list * l, int v, const list_pos p) +{ + // Move elements at position pos and later forward. + bcopy(l->values + p, l->values + p + 1, + sizeof(int) * (l->last_used_pos - p + 1)); + + // Set value. + l->values[p] = v; + + // Increment number of used elements. + l->last_used_pos++; + + // Return the position of the new value. + return p; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Move elements at position pos and later forward. + bcopy(l->values + p + 1, l->values + p, sizeof(int) * (l->last_used_pos - p)); + + // Decrement number of used elements. + l->last_used_pos--; + + // p now refers to the position after the removed element. + return p; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. + * + * Returns: Nothing. + */ +void list_kill(list * l) +{ + free(l->values); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * + * Iterates over the list and print each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list * l) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + printf("[%d]\n",list_inspect(l, p)); + p = list_next(l, p); + } +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // The position is valid if it is between 0 and last_used_pos + 1, inclusive. + return p >= 0 && p <= l->last_used_pos+1; + +} + +// ===========INTERNAL FUNCTIONS USED BY int_list_array_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"last_used_pos\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(l), l->last_used_pos, l->array_size, PTR2ADDR(l->values)); +} + +/** + * print_values() - Print a node containing all positions in the array. + * @indent_level: Indentation level. + * @l: List to inspect. + * @max_values_to_print: Maximum number of values to output. + * + * Will print dot code to display each value in the array, up to + * max_values_to_print. If there are more values to print, adds an + * ellipsis (...) at the end. + * + * Returns: Nothing. + */ +void print_values(int indent_level, const list *l, int max_values_to_print) +{ + int values_to_print = l->array_size; + int truncated = false; + if (values_to_print > max_values_to_print) { + values_to_print = max_values_to_print; + truncated = true; + } + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(l->values)); + for (int i=0; i < values_to_print; i++) { + printf("%02d\\n%d", i, l->values[i]); + if (i < values_to_print - 1) { + printf("|"); + } + } + if (truncated) { + // Add ellipsis + printf("|\\n...\\n"); + } + printf("\"]\n"); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edge(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->values, "v", "values", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the List content + iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the values array. Limit the output to 10 elements. + print_values(il, l, 10); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, l); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/int_list_array/int_list_array_mwe1.c b/OU3/src/int_list_array/int_list_array_mwe1.c new file mode 100644 index 0000000..d222f84 --- /dev/null +++ b/OU3/src/int_list_array/int_list_array_mwe1.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +int main(void) +{ + printf("%s, %s %s: Create typed integer list, implemented as an array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l); + + // Insert the value 2 at the second position in the list. + list_insert(l, 2, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_list_array/int_list_array_mwe1i.c b/OU3/src/int_list_array/int_list_array_mwe1i.c new file mode 100644 index 0000000..4b67328 --- /dev/null +++ b/OU3/src/int_list_array/int_list_array_mwe1i.c @@ -0,0 +1,97 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + printf("Empty list from the outside.\n"); + list_print(l); + print_internal_with_cut_lines(l, "Empty list showing the internals"); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List from the outside after inserting one value:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting one value showing the internals"); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List from the outside after inserting second value at the end:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals"); + + printf("List after removing first element:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after removing first element showing the internals"); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_list_array/int_list_array_test.c b/OU3/src/int_list_array/int_list_array_test.c new file mode 100644 index 0000000..cf7fe4c --- /dev/null +++ b/OU3/src/int_list_array/int_list_array_test.c @@ -0,0 +1,1145 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2023-01-21: v1.0. First public version. This is effectively a copy of the int_list_test.c + * file. + * 2023-03-23: v1.1. Renamed list_pos_are_equal to list_pos_is_equal. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Run the code + list *l = list_empty(); + + // l should be non-NULL + if (l != NULL) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Run the code + list *l = list_empty(); + + // The list returned by empty() should be is_empty() + if (list_is_empty(l) == true) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Run the code + list *l = list_empty(); + + // first(l) should be == end(l) for an empty list + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(); + // Insert an element at the only position in the empty list + list_insert(l, v, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(); + // Insert the first element + list_pos p = list_insert(l, v1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, v2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (!list_is_empty(l)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (!list_pos_is_equal(l, list_first(l), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, 24, list_first(l)); + + // The returned position should be first in the modified list + if (list_pos_is_equal(l, p, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int read_value = list_inspect(l, list_first(l)); + + if (value_equal(read_value, val)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", read_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (!list_pos_is_equal(l, list_next(l, p), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (!list_pos_is_equal(l, list_prev(l, p), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (prev_is_inv_next(l, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (next_is_inv_prev(l, list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that next is inverse of prev on end() + if (list_is_empty(l)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (list_pos_is_equal(l, list_end(l), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_first(l)); + + // Returned position should be == first(l) + if (list_pos_is_equal(l, p, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_end(l)); + + // Returned position should be == prev(end(l)) + if (list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (prev_is_inv_next(l, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (next_is_inv_prev(l, list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Insert new element at end + list_insert(l, v[1], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Insert new element at end + list_insert(l, v[2], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Insert new element at next(first()) + list_insert(l, v[1], list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the end of the list + list_insert(l, n, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (value_equal(i + 1, list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", i + 1, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the beginning of the list + list_insert(l, n, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (value_equal(n - i, list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + n - i, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_list_array datatype.\n", __FILE__, + VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU3/src/int_queue/.gitignore b/OU3/src/int_queue/.gitignore new file mode 100644 index 0000000..824be8c --- /dev/null +++ b/OU3/src/int_queue/.gitignore @@ -0,0 +1,2 @@ +int_queue_example_internal +int_queue_example diff --git a/OU3/src/int_queue/Makefile b/OU3/src/int_queue/Makefile new file mode 100644 index 0000000..44a2320 --- /dev/null +++ b/OU3/src/int_queue/Makefile @@ -0,0 +1,34 @@ +MWE = int_queue_mwe1 int_queue_mwe1i int_queue_example int_queue_example_internal + +SRC = int_queue.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_queue_mwe1: int_queue_mwe1.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_mwe1i: int_queue_mwe1i.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_example: int_queue_example.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_example_internal: int_queue_example_internal.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: int_queue_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/int_queue/int_queue.c b/OU3/src/int_queue/int_queue.c new file mode 100644 index 0000000..51e521f --- /dev/null +++ b/OU3/src/int_queue/int_queue.c @@ -0,0 +1,310 @@ +#include +#include +#include + +#include + +/* + * Implementation of an integer queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores integers directly and does not use + * dynamic memory. Thus, the clean-up function queue_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic queue implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +/** + * queue_empty() - Create an empty queue. + * + * Returns: A new, empty, queue. + */ +queue queue_empty(void) +{ + queue q; + q.first_free_pos = 0; + // Set all elements to zero. Not really needed, but otherwise + // the output from print_internal becomes unreadable. + for (int i = 0; i < sizeof(q.elements)/sizeof(q.elements[0]); i++) { + q.elements[i] = 0; + } + return q; +} + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue q) +{ + // The queue is empty if no positions are occupied with elements, + // i.e., the first free position is zero + return q.first_free_pos == 0; +} + +/** + * queue_enqueue() - Put a value at the end of a queue. + * @q: Queue to manipulate. + * @v: Value (integer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue queue_enqueue(queue q, int v) +{ + // Store value at first free position. + q.elements[q.first_free_pos] = v; + // Update first free position. + q.first_free_pos++; + + // Return the new queue. + return q; +} + +/** + * queue_dequeue() - Remove the element at the beginning of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue queue_dequeue(queue q) +{ + if (queue_is_empty(q)) { + fprintf(stderr, "queue_dequeue: Warning: dequeue on empty queue\n"); + } else { + // Shift all elements one step to the left + for (int i = 0; i < q.first_free_pos - 1; i++) { + q.elements[i] = q.elements[i + 1]; + } + // Update the position of the first free element + q.first_free_pos--; + } + return q; +} + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the front of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +int queue_front(const queue q) +{ + if (queue_is_empty(q)) { + fprintf(stderr, "queue_front: Warning: front on empty queue\n"); + } + return q.elements[0]; +} + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Does nothing since the queue does not use any dynamic + * memory. Included for symmetry with generic queue.h. + * + * Returns: Nothing. + */ +void queue_kill(queue q) +{ + // Do nothing. +} + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * + * Iterates over the queue and prints each integer. + * + * Returns: Nothing. + */ +void queue_print(const queue q) +{ + printf("{ "); + // Print elements from the top down. + for (int i = 0; i < q.first_free_pos; i++) { + printf("[%d]", q.elements[i]); + if (i < q.first_free_pos - 1) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i q [style=invis] " + "// Dummy line to place description above\n"); +} + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the queue and outputs dot code that shows the + * internal structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph QUEUE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + // Output the head node + print_head_node(il, max_elems, q); + + // Output a dummy line to place the description at the top + print_desc_edge(il); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/int_queue/int_queue_example.c b/OU3/src/int_queue/int_queue_example.c new file mode 100644 index 0000000..1a3416e --- /dev/null +++ b/OU3/src/int_queue/int_queue_example.c @@ -0,0 +1,50 @@ +#include +#include + +#include + +/* + * A small example that uses an integer queue. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-16: First public version. + */ + +int main(void) +{ + // Create the queue. + queue q = queue_empty(); + + // The queue should be empty. Is it? + if (queue_is_empty(q)) { + printf("The newly created queue is empty.\n"); + } else { + printf("The newly created queue is NOT empty.\n"); + } + + for (int i = 11; i <= 13; i++) { + printf("Add the value %d at the end of the queue.\n", i); + // Put some values in the queue. + q = queue_enqueue(q, i); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + // Remove one element from the queue. + printf("Remove one element.\n"); + q = queue_dequeue(q); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_queue/int_queue_example_internal.c b/OU3/src/int_queue/int_queue_example_internal.c new file mode 100644 index 0000000..b4110fb --- /dev/null +++ b/OU3/src/int_queue/int_queue_example_internal.c @@ -0,0 +1,99 @@ +#include +#include + +#include + +// Size of internal string buffer +#define BUFLEN 400 + +/* + * A small example that uses an integer queue. + * + * Generates two kinds of output: + * 1) The 'outside' view showing the stored values only. + * 2) The 'inside' view showing the internal organization. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-24: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-24" + +// Print a message, then the queue and wait for the user to press return. +void print_and_wait(const queue q, const char *msg) +{ + printf("\nThe output from queue_print() shows the values stored in the queue\n"); + printf("\n%s\n", msg); + queue_print(q); + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_and_wait(queue q, const char *desc) +{ + // Print starting marker line. + printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n"); + printf("\n\n1) Copy the lines between the cut marks\n" + "2) Paste into the left half of the browser window.\n" + "3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n"); + printf("\n--- CUT HERE ---\n\n"); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, desc, 0, 10); + + // Print ending marker line + printf("\n--- END CUT HERE ---\n\n"); + + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +int main(void) +{ + // Message buffer + char buf[BUFLEN]; + + printf("%s, %s %s: Example use of a typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue q = queue_empty(); + + // Call print functions to show the content of the queue + print_and_wait(q, "The queue is empty"); + print_internal_and_wait(q, "The queue is empty"); + + for (int i = 11; i <= 13; i++) { + q = queue_enqueue(q, i); + snprintf(buf, BUFLEN, "The value %d was added to the queue", i); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + // Remove one element from the queue. + q = queue_dequeue(q); + snprintf(buf, BUFLEN, "The front element was removed from the queue"); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_queue/int_queue_mwe1.c b/OU3/src/int_queue/int_queue_mwe1.c new file mode 100644 index 0000000..8a5b416 --- /dev/null +++ b/OU3/src/int_queue/int_queue_mwe1.c @@ -0,0 +1,56 @@ +#include +#include + +#include + +/* + * Minimum working example for int_queue.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-10" + +int main(void) +{ + printf("%s, %s %s: Create typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue q = queue_empty(); + printf("--Empty QUEUE--\n"); + queue_print(q); + + + for (int i = 11; i <= 13; i++) { + // Put some values in the queue. + q = queue_enqueue(q, i); + printf("--QUEUE during insert--\n"); + queue_print(q); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + printf("--QUEUE after enqueue, before dequeueing--\n"); + queue_print(q); + + // Remove one element from the queue. + q = queue_dequeue(q); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_queue/int_queue_mwe1i.c b/OU3/src/int_queue/int_queue_mwe1i.c new file mode 100644 index 0000000..2a8c57b --- /dev/null +++ b/OU3/src/int_queue/int_queue_mwe1i.c @@ -0,0 +1,104 @@ +#include +#include + +#include + +/* + * Minimum working example for int_queue.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: Adapted from int_queue_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-10" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(queue s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(s, desc, 0, 10); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue q=queue_empty(); + + printf("Empty queue from the outside:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Empty queue showing the inside"); + + for (int i = 11; i <= 13; i++) { + // Put values in the queue. + q = queue_enqueue(q, i); + } + + printf("Queue for the outside after adding 3 elements:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after adding 3 elements"); + + // Remove one element from queue. + q = queue_dequeue(q); + + printf("Queue for the outside after removing 1 element:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after removing 1 element"); + + // Pop remaining elements + while (!queue_is_empty(q)) { + q = queue_dequeue(q); + } + + printf("Queue from the outside after removing all elements:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after removing all elements"); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue_mwe1.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_stack/Makefile b/OU3/src/int_stack/Makefile new file mode 100644 index 0000000..9611338 --- /dev/null +++ b/OU3/src/int_stack/Makefile @@ -0,0 +1,28 @@ +MWE = int_stack_mwe1 int_stack_mwe1i + +SRC = int_stack.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_stack_mwe1: int_stack_mwe1.c int_stack.c + gcc -o $@ $(CFLAGS) $^ + +int_stack_mwe1i: int_stack_mwe1i.c int_stack.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: int_stack_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/int_stack/int_stack.c b/OU3/src/int_stack/int_stack.c new file mode 100644 index 0000000..0012c3d --- /dev/null +++ b/OU3/src/int_stack/int_stack.c @@ -0,0 +1,316 @@ +#include +#include +#include + +#include + +/* + * Implementation of an integer stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores integers directly and does not use + * dynamic memory. Thus, the clean-up function stack_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic stack implementations. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se) + * Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2022-03-24: First public version. + * v1.1 2024-04-10: Added print_internal. + * v1.2 2024-05-10: Added/updated print_internal with improved encapsulation. + */ + +/** + * stack_empty() - Create an empty stack. + * + * Returns: An empty stack. + */ +stack stack_empty(void) +{ + stack s; + s.first_free_pos = 0; + // Set all elements to zero. Not really needed, but otherwise + // the output from print_internal becomes unreadable. + for (int i = 0; i < sizeof(s.elements)/sizeof(s.elements[0]); i++) { + s.elements[i] = 0; + } + return s; +} + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack s) +{ + // The stack is empty if no positions are occupied with elements, + // i.e., the first free position is zero + return s.first_free_pos == 0; +} + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (integer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_push(stack s, int v) +{ + // Store value at first free position. + s.elements[s.first_free_pos] = v; + // Update first free position. + s.first_free_pos++; + + // Return the new stack. + return s; +} + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_pop(stack s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_pop: Warning: pop on empty stack\n"); + } else { + // We only have to decrease the first free position to + // indicate that the element that used to be on top of the + // stack is now free for use. + s.first_free_pos--; + } + return s; +} + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The integer at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +int stack_top(const stack s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_top: Warning: top on empty stack\n"); + } + return s.elements[s.first_free_pos - 1]; +} + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Does nothing since the stack does not use any dynamic + * memory. Included for symmetry with generic stack.h. + * + * Returns: Nothing. + */ +void stack_kill(stack s) +{ + // Do nothing. +} + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * + * Iterates over the stack and prints each integer. + * + * Returns: Nothing. + */ +void stack_print(const stack s) +{ + printf("{ "); + // Print elements from the top down. + for (int i = s.first_free_pos - 1; i >= 0; i--) { + printf("[%d]", s.elements[i]); + if (i>0) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i s [style=invis] " + "// Dummy line to place description above\n"); +} + +/** + * stack_print_internal() - Print the internal structure of the stack in dot format. + * @s: Stack to inspect. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the stack and outputs dot code that shows the + * internal structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph STACK_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + // Output the head node + print_head_node(il, max_elems, s); + + // Output a dummy line to place the description at the top + print_desc_edge(il); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/int_stack/int_stack_mwe1.c b/OU3/src/int_stack/int_stack_mwe1.c new file mode 100644 index 0000000..cd5039d --- /dev/null +++ b/OU3/src/int_stack/int_stack_mwe1.c @@ -0,0 +1,48 @@ +#include +#include + +#include + +/* + * Minimum working example for int_stack.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2022-03-24: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-01-14" + +int main(void) +{ + printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. + stack s=stack_empty(); + + for (int i=1; i<=3; i++) { + // Push value on stack. + s = stack_push(s, i); + } + + printf("--STACK before popping--\n"); + stack_print(s); + + // Pop element from stack. + s=stack_pop(s); + + printf("--STACK after popping--\n"); + stack_print(s); + + // Kill stack. Strictly not necessary for int_stack since it + // does not use any dynamic memory. Included for symmetry with + // stack.c. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/int_stack/int_stack_mwe1i.c b/OU3/src/int_stack/int_stack_mwe1i.c new file mode 100644 index 0000000..dcd4c97 --- /dev/null +++ b/OU3/src/int_stack/int_stack_mwe1i.c @@ -0,0 +1,103 @@ +#include +#include + +#include + +/* + * Minimum working example for int_stack.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-10: Adapted from int_stack_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-10" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(stack s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, desc, 0, 10); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack s=stack_empty(); + + printf("Empty stack from the outside:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Empty stack showing the inside"); + + for (int i=1; i<=3; i++) { + // Push value on stack. + s = stack_push(s, 10 + i); + } + + printf("Stack for the outside after pushing 3 elements:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after pushing 3 elements"); + + // Pop one element from stack. + s = stack_pop(s); + + printf("Stack for the outside after popping 1 element:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after popping 1 element"); + + // Pop remaining elements + while (!stack_is_empty(s)) { + s = stack_pop(s); + } + + printf("Stack from the outside after popping all elements:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after popping all elements"); + + // Kill stack. Strictly not necessary for int_stack since it + // does not use any dynamic memory. Included for symmetry with + // stack_mwe1.c. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/list/Makefile b/OU3/src/list/Makefile new file mode 100644 index 0000000..ea97d56 --- /dev/null +++ b/OU3/src/list/Makefile @@ -0,0 +1,68 @@ +MWE = list_mwe1 list_mwe2 list_mwe1i list_mwe2i +TEST = list_test1 list_test2 + +SRC = list.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +test: list_test1 list_test2 + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +list_mwe1: list_mwe1.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe2: list_mwe2.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe1i: list_mwe1i.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe2i: list_mwe2i.c list.c + gcc -o $@ $(CFLAGS) $^ + +memtest_mwe1: list_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe2: list_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe1i: list_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe2i: list_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< + +list_test1: list_test1.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_test2: list_test2.c list.c + gcc -o $@ $(CFLAGS) $^ + +test_run1: list_test1 + # Run the test + ./$< + +test_run2: list_test2 + # Run the test + ./$< + +memtest_test1: list_test1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test2: list_test2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtests: memtest_mwe1 memtest_mwe2 memtest_mwe1i memtest_mwe2i memtest_test1 memtest_test2 diff --git a/OU3/src/list/README.md b/OU3/src/list/README.md new file mode 100644 index 0000000..3d203fd --- /dev/null +++ b/OU3/src/list/README.md @@ -0,0 +1,111 @@ +# Dubbellänkad lista +En två-cellsimplementation av ADT:n _Lista_. + +## Minneshantering + +Följande kodsnutt kommer skapa en lista med ett element som har värdet 5. I +detta exempel så allokeras inget minne dynamiskt, därför behöver vi inte heller +ta hand om något minne efter vi använt klart listan. + +```c +int val = 5; +list *l = list_empty(NULL); +list_insert(l, &val, list_first(l)); +``` + +Vi kan skriva om ovan exempel så att minne allokeras på heapen, men då måste vi +manuellt komma ihåg att avallokera detta minne efter användning. + +```c +int *val = malloc(sizeof(int)); +*val = 5; + +list *l = list_empty(NULL); +list_insert(l, val, list_first(l)); +... +list_kill(l); +free(val); +``` + +Om vi vill allokera minne dynamiskt, men vill att listan själva ska hantera +avallokering av minne (e.g. vid `list_remove`) så behöver vi ange en +minneshanteringsfunktion: +```c +int *val = malloc(sizeof(int)); +*val = 5; + +list *l = list_empty(free); +list_insert(l, val, list_first(l)); +... +list_kill(l); +``` + +I exemplet ovan kommer `list_kill(l)` att anropa `free` för varje element i +listan, och därmed sköta minneshanteringen åt oss. + +### Egen free-funktion + +Det är också möjligt att definiera en egen funktion som ska sköta +avallokeringen. Till exempel kanske vi vill skriva ut varje värde som +avallokeras i debugging-syfte. Då kan vi skriva en enkel wrapper-funktion för +`free` på följande sätt: + +```c +static void free_and_print(void *data) +{ + printf("Free'ing [%d]\n", *(int*)data); + free(data); +} +``` + +Denna funktion skulle då kunna användas på följande sätt: + +```c +list *l = list_empty(free_and_print); +int *val = malloc(sizeof(int)); +*val = 5; +list_insert(l, val, list_first(l)); +list_kill(l); +``` + +Denna kodsnutt skulle då skriva ut `Free'ing [5]`. + + +## Utskrift + +För att skriva ut listan, t.ex. vid debugging eller för att visa innehållet för +en användare så kan vi definiera en egen funktion för att presentera tillståndet +hos datatypen. Det enklaste fallet är om vi enbart vill skriva ut, e.g., en +lista med heltal där varje värde hamnar på en egen rad: + +```c +static void print_ints(const void *data) +{ + printf("%d\n", *(int*)data); +} +``` + +Notera att inparametern är av typen `const`. `const` indikerar att pekaren bara +får läsas, ej manipuleras. Vi vill inte att en print-funktion ska kunna ändra på +listans innehåll. + +`print_ints` kan nu användas på följande sätt: + +```c +list *l = list_empty(free); +int *val = malloc(sizeof(int)); +*val = 5; +list_insert(l, val, list_first(l)); + +list_print(l, print_ints); +``` + +Detta exempel skulle skriva ut `5` på en egen rad. + + +# Minimal working example + +Mycket av det som behandlats ovan sammanfattas i följande minimal working +example: + +Se [list_mwe1.c](list_mwe1.c) och [list_mwe2.c](list_mwe2.c). diff --git a/OU3/src/list/list.c b/OU3/src/list/list.c new file mode 100644 index 0000000..a09391a --- /dev/null +++ b/OU3/src/list/list.c @@ -0,0 +1,620 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic, undirected list for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-02-25: Renamed list header fields head and tail. + * v1.4 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-03-14: Added list_print_internal to output dot code for visualization. + * v2.1 2024-05-10: Added/updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +/* + * The list elements are implemented as two-cells with forward and + * backward links and a void * for the value. The list uses two border + * cells at the start and end of the list. + */ +typedef struct cell { + struct cell *next; + struct cell *prev; + void *val; +} cell; + +struct list { + cell *head; + cell *tail; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * list_empty() - Create an empty list. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +list *list_empty(kill_function kill_func) +{ + // Allocate memory for the list head. + list *l = calloc(1, sizeof(list)); + + // Allocate memory for the border cells. + l->head = calloc(1, sizeof(cell)); + l->tail = calloc(1, sizeof(cell)); + + // Set consistent links between border elements. + l->head->next = l->tail; + l->tail->prev = l->head; + + // Store the kill function. + l->kill_func = kill_func; + + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l) +{ + // List is empty if there are no cells between head and tail. + return (l->head->next == l->tail); +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l) +{ + // First position is position of first element. + return l->head->next; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l) +{ + // Last position is position *after* last element. + return l->tail; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); + } + return p->next; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p->prev; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *list_inspect(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->val; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, void *v, const list_pos p) +{ + // Allocate memory for a new cell. + list_pos e = malloc(sizeof(cell)); + + // Store the value. + e->val = v; + // Add links to/from the new cell. + e->next = p; + e->prev = p->prev; + p->prev = e; + e->prev->next = e; + + // Return the position of the new cell. + return e; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Remember return position. + list_pos next_pos = p->next; + // Link past this element. + p->prev->next = p->next; + p->next->prev = p->prev; + + // Call kill_func if registered. + if (l->kill_func != NULL) { + // Return any user-allocated memory for the value. + l->kill_func(p->val); + } + // Return the memory allocated to the cell itself. + free(p); + // Return the position of the next element. + return next_pos; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element + * values. + * + * Returns: Nothing. + */ +void list_kill(list *l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + list_pos p = list_first(l); + + // Remove first element until list is empty. + while (!list_is_empty(l)) { + p = list_remove(l, p); + } + + // Free border elements and the list head. + free(l->head); + free(l->tail); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void list_print(const list *l, inspect_callback print_func) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + printf("( "); + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + print_func(list_inspect(l, p)); + // Advance to next position. + p = list_next(l, p); + // Print separator unless at end. + if (!list_pos_is_equal(l, p, list_end(l))) { + printf(", "); + } + } + printf(" )\n"); +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // Iterate over all positions in l. + list_pos q = list_first(l); + while (!list_pos_is_equal(l, q, list_end(l))) { + if (list_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = list_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l->tail), + PTR2ADDR(l)); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edges(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); + print_edge(indent_level, l, l->tail, "t", "tail", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const list_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%04lx|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const list *l, const list_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + print_edge(indent_level, p, p->prev, "p", "prev", NULL); + + // Ignore val ptr for head and tail nodes. + if (p == l->head || p == l->tail) { + return; + } + if (l->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + list_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = l->head; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/list/list_mwe1.c b/OU3/src/list/list_mwe1.c new file mode 100644 index 0000000..8d7d20d --- /dev/null +++ b/OU3/src/list/list_mwe1.c @@ -0,0 +1,103 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +static void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer list without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(NULL); + + // Create a dynamic integer with value 5. + int *val = int_create(5); + + // Insert the value first in the list. + list_insert(l, val, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l, print_int); + + // Allocate space for another integer. + val = int_create(8); + // Insert the value last in the list. + list_insert(l, val, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l, print_int); + + // Allocate space for a third integer. + val = int_create(2); + // Insert the value at the second position in the list. + list_insert(l, val, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l, print_int); + + // Remove first element. + list_pos p=list_first(l); + int *v=list_inspect(l, p); + // Remove element from list. + list_remove(l, p); + // Free element content. + int_kill(v); + + printf("List after removing first element:\n"); + list_print(l, print_int); + + // Empty the list. + while (!list_is_empty(l)) { + list_pos p=list_first(l); + int *v=list_inspect(l,p); + int_kill(v); + list_remove(l,p); + } + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/list/list_mwe1i.c b/OU3/src/list/list_mwe1i.c new file mode 100644 index 0000000..e350614 --- /dev/null +++ b/OU3/src/list/list_mwe1i.c @@ -0,0 +1,131 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "borrows" the payload memory, i.e., the user of the list is + * responsible for deallocating the payload memory. See list_mwe2i.c + * for a version where the list takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer list without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the list. + list *l = list_empty(NULL); + + printf("Empty list from the outside:\n"); + list_print(l, print_int); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + // Insert the value 5 first in the list. + list_insert(l, int_create(5), list_first(l)); + // Insert the value 8 last in the list. + list_insert(l, int_create(8), list_end(l)); + // Insert the value 2 at the second position in the list. + list_insert(l, int_create(2), list_next(l, list_first(l))); + + printf("List from the outside after inserting 3 values:\n"); + list_print(l, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the List after inserting 3 values.\n" + "Red lines are used for the list payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the list, i.e., the payload\n" + "memory will NOT be deallocated by the list.\n\n" + "See list_mwe2i for a list example\nthat owns the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Empty the list. + while (!list_is_empty(l)) { + list_pos p=list_first(l); + int *v=list_inspect(l,p); + int_kill(v); + list_remove(l,p); + } + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/list/list_mwe2.c b/OU3/src/list/list_mwe2.c new file mode 100644 index 0000000..0820443 --- /dev/null +++ b/OU3/src/list/list_mwe2.c @@ -0,0 +1,92 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.21 2024-01-16: Fix include to be with brackets, not citation marks. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +static void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer list with custom kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(int_kill); + + // Create a dynamic integer with value 5. + int *val = int_create(5); + + // Insert the value first in the list. + list_insert(l, val, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l, print_int); + + // Allocate space for another integer. + val = int_create(8); + // Insert the value last in the list. + list_insert(l, val, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l, print_int); + + // Allocate space for a third integer. + val = int_create(2); + // Insert the value at the second position in the list. + list_insert(l, val, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l, print_int); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l, print_int); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/list/list_mwe2i.c b/OU3/src/list/list_mwe2i.c new file mode 100644 index 0000000..8866d32 --- /dev/null +++ b/OU3/src/list/list_mwe2i.c @@ -0,0 +1,125 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "owns" the payload memory, i.e., the list takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See list_mwe1i.c for a version + * where the list does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer list with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the list. + list *l = list_empty(int_kill); + + printf("Empty list from the outside:\n"); + list_print(l, print_int); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + // Insert the value 5 first in the list. + list_insert(l, int_create(5), list_first(l)); + // Insert the value 8 last in the list. + list_insert(l, int_create(8), list_end(l)); + // Insert the value 2 at the second position in the list. + list_insert(l, int_create(2), list_next(l, list_first(l))); + + printf("List from the outside after inserting 3 values:\n"); + list_print(l, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the List after inserting 3 values.\n" + "Red lines are used for the list payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the list, i.e., the payload\n" + "memory WILL be deallocated by the list.\n\n" + "See list_mwe1i for a list example\nthat borrows the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Kill the list, including the payload. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/list/list_test1.c b/OU3/src/list/list_test1.c new file mode 100644 index 0000000..ea4e0dc --- /dev/null +++ b/OU3/src/list/list_test1.c @@ -0,0 +1,1190 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Test program for the list implementation in list.c. This version + * uses a free-handler, i.e., the list will be responsible for + * deallocating any elements we allocate and put in it. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * v1.0 2023-01-21: First public version. + * v1.1 2023-03-21: Removed empty if statements in test code. + * v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(int_kill); + // Make a dynamic copy of v. + int *q = int_create(v); + // Insert an element at the only position in the empty list + list_insert(l, q, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(int_kill); + // Make dynamic copies of v1 and v2 + int *q1 = int_create(v1); + int *q2 = int_create(v2); + // Insert the first element + list_pos p = list_insert(l, q1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, q2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(int_kill); + // Create a dynamic integer + int *q = int_create(24); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, q, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int *stored_ptr = list_inspect(l, list_first(l)); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of element value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Make dynamic copy of element to insert + int *q1 = int_create(v[1]); + + // Insert new element at end + list_insert(l, q1, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Make dynamic copy of element to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Make dynamic copy of value to insert + int *q2 = int_create(v[2]); + + // Insert new element at end + list_insert(l, q2, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Make dynamic copy of value to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Make dynamic copy of value to insert + int *q1 = int_create(v[1]); + + // Insert new element at next(first()) + list_insert(l, q1, list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(int_kill); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the end of the list + list_insert(l, q, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(i + 1, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(int_kill); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the beginning of the list + list_insert(l, q, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(n - i, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(int_kill); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(int_kill); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(int_kill); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(int_kill); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the generic list.\n" + "Uses the standard free-handler to handle deallocation of dynamic memory.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Uses code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU3/src/list/list_test2.c b/OU3/src/list/list_test2.c new file mode 100644 index 0000000..7bfcd8e --- /dev/null +++ b/OU3/src/list/list_test2.c @@ -0,0 +1,1308 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Test program for the list implementation in list.c. This version + * does not use a free-handler, i.e., the list will not be responsible + * for deallocating any allocated elements stored in it. Instead, the + * responsibility remains with the user of the list. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * v1.0 2023-01-21: First public version. + * v1.1 2023-03-21: Removed empty if statements in test code. + * v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * kill_all_element_values() - kill all element values stored in the list + * @l - List to operate on + * + * Returns: A pointer to the modified list + * + * The function traverses the list and calls int_kill for the value (void *) + * stored in each element. WARNING: This should only be executed + * immediately before the list is killed since all element values are + * UNDEFINED after the call. + */ +list *kill_all_element_values(list *l) +{ + // Iterate over all elements + list_pos p=list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + // Read the stored pointer value + void *q = list_inspect(l, p); + // Free its memory + int_kill(q); + // Advance to the next element + p = list_next(l, p); + } + return l; +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(NULL); + // Make a dynamic copy of v. + int *q = int_create(v); + // Insert an element at the only position in the empty list + list_insert(l, q, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(NULL); + // Make dynamic copies of v1 and v2 + int *q1 = int_create(v1); + int *q2 = int_create(v2); + // Insert the first element + list_pos p = list_insert(l, q1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, q2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(NULL); + // Create a dynamic integer + int *q = int_create(24); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, q, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int *stored_ptr = list_inspect(l, list_first(l)); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of element value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Make dynamic copy of element to insert + int *q1 = int_create(v[1]); + + // Insert new element at end + list_insert(l, q1, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Make dynamic copy of element to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Make dynamic copy of value to insert + int *q2 = int_create(v[2]); + + // Insert new element at end + list_insert(l, q2, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Make dynamic copy of value to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Make dynamic copy of value to insert + int *q1 = int_create(v[1]); + + // Insert new element at next(first()) + list_insert(l, q1, list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(NULL); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the end of the list + list_insert(l, q, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(i + 1, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(NULL); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the beginning of the list + list_insert(l, q, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(n - i, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(NULL); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(NULL); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(NULL); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(NULL); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the generic list.\n" + "Does not use any free-handler to handle deallocation of dynamic memory.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Uses code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU3/src/queue/.gitignore b/OU3/src/queue/.gitignore new file mode 100644 index 0000000..9fd76b1 --- /dev/null +++ b/OU3/src/queue/.gitignore @@ -0,0 +1,2 @@ +queue_example +queue_example_internal diff --git a/OU3/src/queue/Makefile b/OU3/src/queue/Makefile new file mode 100644 index 0000000..5795e44 --- /dev/null +++ b/OU3/src/queue/Makefile @@ -0,0 +1,49 @@ +MWE = queue_mwe1 queue_mwe2 queue_mwe1i queue_mwe2i queue_example queue_example_internal + +SRC = queue.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +queue_example_internal: queue_example_internal.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_example: queue_example.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe1: queue_mwe1.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe2: queue_mwe2.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe1i: queue_mwe1i.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe2i: queue_mwe2i.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: queue_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: queue_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: queue_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: queue_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/queue/README.md b/OU3/src/queue/README.md new file mode 100644 index 0000000..f6746dd --- /dev/null +++ b/OU3/src/queue/README.md @@ -0,0 +1,11 @@ +# Kö +En implementation av ADT:n _Kö_ baserad på en dubbel-länkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + +# Minimal working example + +Se [queue_mwe1.c](queue_mwe1.c) och [queue_mwe2.c](queue_mwe2.c). diff --git a/OU3/src/queue/queue.c b/OU3/src/queue/queue.c new file mode 100644 index 0000000..3b73bc7 --- /dev/null +++ b/OU3/src/queue/queue.c @@ -0,0 +1,401 @@ +#include +#include +#include + +#include +#include + +/* + * Implementation of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.01 2024-01-16: Fix include to be with brackets, not citation marks. + * v2.0 2024-03-14: Added queue_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +/* + * The queue is implemented using the list abstract datatype. Almost + * everything is done by the list. + */ + +struct queue { + list *elements; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func) +{ + // Allocate the queue head. + queue *q = calloc(1, sizeof(*q)); + // Create an empty list. + q->elements = list_empty(kill_func); + + return q; +} + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q) +{ + return list_is_empty(q->elements); +} + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v) +{ + list_insert(q->elements, v, list_end(q->elements)); + return q; +} + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q) +{ + list_remove(q->elements, list_first(q->elements)); + return q; +} + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q) +{ + return list_inspect(q->elements, list_first(q->elements)); +} + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q) +{ + list_kill(q->elements); + free(q); +} + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func) +{ + printf("{ "); + list_pos pos = list_first(q->elements); + while (pos != list_end(q->elements)) { + print_func(list_inspect(q->elements, pos)); + pos = list_next(q->elements, pos); + if (pos != list_end(q->elements)) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the queue head struct. + * @il: Indentation level. + * @q: Queue to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const queue *q) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"elements\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(q), PTR2ADDR(q->elements), PTR2ADDR(q)); +} + +// Print edges from the queue head to the elements list. +static void print_head_edge(int indent_level, const queue *q) +{ + print_edge(indent_level, q, q->elements, "e", "elements", NULL); +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * queue_print_internal() - Print the queues internal structure in dot format. + * @l: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph QUEUE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "q [label=\"%04lx\" xlabel=\"q\"]\n", PTR2ADDR(q)); + iprintf(il, "q -> m%04lx\n", PTR2ADDR(q)); + } + + // Output the payload nodes before asking the list to print them + // to render them in their own subgraph. + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + list_pos p = list_first(q->elements); + while (!list_pos_is_equal(q->elements, p, list_end(q->elements))) { + print_value_node(il, list_inspect(q->elements, p), print_func); + // Advance + p = list_next(q->elements, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_queue_%d { label=\"Queue\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, q); + + if (q->elements) { + list_print_internal(q->elements, print_func, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, q); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/queue/queue_example.c b/OU3/src/queue/queue_example.c new file mode 100644 index 0000000..9782f9b --- /dev/null +++ b/OU3/src/queue/queue_example.c @@ -0,0 +1,68 @@ +#include +#include + +#include + +/* + * A small example that uses the generic queue to store integers. + * Based on int_queue_example.c from int_queue. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-23: First public version. + */ + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + // Create the queue. + queue *q = queue_empty(int_kill); + + // The queue should be empty. Is it? + if (queue_is_empty(q)) { + printf("The newly created queue is empty.\n"); + } else { + printf("The newly created queue is NOT empty.\n"); + } + + for (int i = 11; i <= 13; i++) { + printf("Add the value %d at the end of the queue.\n", i); + int *v = int_create(i); + // Put some values in the queue. + q = queue_enqueue(q, v); + } + + int *v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", *v); + + // Remove one element from the queue. + printf("Remove one element.\n"); + q = queue_dequeue(q); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", *v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/queue/queue_example_internal.c b/OU3/src/queue/queue_example_internal.c new file mode 100644 index 0000000..93d77e4 --- /dev/null +++ b/OU3/src/queue/queue_example_internal.c @@ -0,0 +1,124 @@ +#include +#include + +#include + +// Size of internal string buffer +#define BUFLEN 400 + +/* + * A small example that uses a generic queue to store integers. + * + * Generates two kinds of output: + * 1) The 'outside' view showing the stored values only. + * 2) The 'inside' view showing the internal organization. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-24: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-24" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print a message, then the queue and wait for the user to press return. +void print_and_wait(const queue *q, const char *msg) +{ + printf("\nThe output from queue_print() shows the values stored in the queue\n"); + printf("\n%s\n", msg); + queue_print(q, print_int); + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_and_wait(const queue *q, const char *desc) +{ + // Print starting marker line. + printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n"); + printf("\n\n1) Copy the lines between the cut marks\n" + "2) Paste into the left half of the browser window.\n" + "3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n"); + printf("\n--- CUT HERE ---\n\n"); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("\n--- END CUT HERE ---\n\n"); + + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +int main(void) +{ + // Message buffer + char buf[BUFLEN]; + + printf("%s, %s %s: Example use of a typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q = queue_empty(int_kill); + + // Call print functions to show the content of the queue + print_and_wait(q, "The queue is empty"); + print_internal_and_wait(q, "The queue is empty"); + + for (int i = 11; i <= 13; i++) { + int *v = int_create(i); + q = queue_enqueue(q, v); + snprintf(buf, BUFLEN, "The value %d was added to the queue", i); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + } + + int *w = queue_front(q); + printf("The value at the front of the queue is: %d.\n", *w); + + // Remove one element from the queue. + q = queue_dequeue(q); + snprintf(buf, BUFLEN, "The front element was removed from the queue"); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + + w = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", *w); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/queue/queue_mwe1.c b/OU3/src/queue/queue_mwe1.c new file mode 100644 index 0000000..c807911 --- /dev/null +++ b/OU3/src/queue/queue_mwe1.c @@ -0,0 +1,89 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer queue without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q=queue_empty(NULL); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q=queue_enqueue(q,v); + } + + printf("--QUEUE before dequeueing--\n"); + queue_print(q, print_int); + + // Get value on top of queue. + int *v=queue_front(q); + // Remote element from queue. + q=queue_dequeue(q); + // Return memory + int_kill(v); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q, print_int); + + // Now we must empty the queue and free each value ourselves. + while (!queue_is_empty(q)) { + // Get value from top of queue. + int *v=queue_front(q); + // Remove element from queue. + q=queue_dequeue(q); + // Free value + int_kill(v); + } + // Finally, destroy the bare queue. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/queue/queue_mwe1i.c b/OU3/src/queue/queue_mwe1i.c new file mode 100644 index 0000000..d8f0bed --- /dev/null +++ b/OU3/src/queue/queue_mwe1i.c @@ -0,0 +1,134 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c that shows how the internal + * structure of a queue can be visualized. In this version, the queue + * "borrows" the payload memory, i.e., the user of the queue is + * responsible for deallocating the payload memory. See queue_mwe2i.c + * for a version where the queue takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call queue_print_internal. +void print_internal_with_cut_lines(const queue *q, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer queue without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue *q = queue_empty(NULL); + + printf("Empty queue from the outside:\n"); + queue_print(q, print_int); + print_internal_with_cut_lines(q, "Empty queue showing the internal structure"); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q = queue_enqueue(q, v); + } + + printf("Queue from the outside after inserting 3 values:\n"); + queue_print(q, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Queue after inserting 3 values.\n" + "All data is stored internally using a List.\n" + "Red lines are used for the queue payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the queue, i.e., the payload\n" + "memory will NOT be deallocated by the queue.\n\n" + "See queue_mwe2i for a queue example\nthat owns the payload memory."; + print_internal_with_cut_lines(q, long_desc); + + // Now we must empty the queue and free each value ourselves. + while (!queue_is_empty(q)) { + // Get value from top of queue. + int *v = queue_front(q); + // Remove element from queue. + q = queue_dequeue(q); + // Free value + int_kill(v); + } + // Finally, destroy the bare queue. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/queue/queue_mwe2.c b/OU3/src/queue/queue_mwe2.c new file mode 100644 index 0000000..ad27458 --- /dev/null +++ b/OU3/src/queue/queue_mwe2.c @@ -0,0 +1,75 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer queue with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q = queue_empty(int_kill); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q=queue_enqueue(q,v); + } + + printf("--QUEUE before dequeueing--\n"); + queue_print(q, print_int); + + // Dequeue one element. The payload memory will be automatically returned. + queue_dequeue(q); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q, print_int); + + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/queue/queue_mwe2i.c b/OU3/src/queue/queue_mwe2i.c new file mode 100644 index 0000000..93d851a --- /dev/null +++ b/OU3/src/queue/queue_mwe2i.c @@ -0,0 +1,126 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c that shows how the internal + * structure of a queue can be visualized. In this version, the queue + * "owns" the payload memory, i.e., the queue takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See queue_mwe1i.c for a version + * where the queue does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call queue_print_internal. +void print_internal_with_cut_lines(const queue *q, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer queue with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue *q = queue_empty(int_kill); + + printf("Empty queue from the outside:\n"); + queue_print(q, print_int); + print_internal_with_cut_lines(q, "Empty queue showing the internal structure"); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q = queue_enqueue(q, v); + } + + printf("Queue from the outside after inserting 3 values:\n"); + queue_print(q, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Queue after inserting 3 values.\n" + "All data is stored internally using a List.\n" + "Red lines are used for the queue payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the queue, i.e., the payload\n" + "memory WILL be deallocated by the queue.\n\n" + "See queue_mwe1i for a queue example\nthat borrows the payload memory."; + print_internal_with_cut_lines(q, long_desc); + + // Destroy the queue and its content. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/stack/Makefile b/OU3/src/stack/Makefile new file mode 100644 index 0000000..d2db60b --- /dev/null +++ b/OU3/src/stack/Makefile @@ -0,0 +1,43 @@ +MWE = stack_mwe1 stack_mwe2 stack_mwe1i stack_mwe2i + +SRC = stack.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +stack_mwe1: stack_mwe1.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe2: stack_mwe2.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe1i: stack_mwe1i.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe2i: stack_mwe2i.c stack.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: stack_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: stack_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: stack_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: stack_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU3/src/stack/README.md b/OU3/src/stack/README.md new file mode 100644 index 0000000..0322013 --- /dev/null +++ b/OU3/src/stack/README.md @@ -0,0 +1,11 @@ +# Stack +En implementation av ADT:n _Stack_ implementerad som en enkellänkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + +# Minimal working example + +Se [stack_mwe1.c](stack_mwe1.c) och [stack_mwe2.c](stack_mwe2.c). diff --git a/OU3/src/stack/int_stack_test_marc.c b/OU3/src/stack/int_stack_test_marc.c new file mode 100644 index 0000000..c99cd2f --- /dev/null +++ b/OU3/src/stack/int_stack_test_marc.c @@ -0,0 +1,77 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ + +#include +/*#include */ + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the stack_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // create an empty stack + stack s = stack_empty(); + + + // l should be non-NULL + if (s == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(s); + fprintf(stderr,"done.\n"); +} + + + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU3/src/stack/stack.c b/OU3/src/stack/stack.c new file mode 100644 index 0000000..cd3a869 --- /dev/null +++ b/OU3/src/stack/stack.c @@ -0,0 +1,463 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2022-03-24: Minor update to always take care of returned pointer. + * v2.0 2024-03-14: Added stack_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The stack elements are implemented as one-cells with forward and + * links. The stack has a single element pointer. + */ +typedef struct cell { + struct cell *next; + void *val; +} cell; + +struct stack { + cell *top; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * stack_empty() - Create an empty stack. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new stack. + */ +stack *stack_empty(kill_function kill_func) +{ + // Allocate memory for stack structure. + stack *s = calloc(1, sizeof(stack)); + s->top = NULL; + s->kill_func = kill_func; + + return s; +} + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack *s) +{ + return s->top == NULL; +} + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (pointer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_push(stack *s, void *v) +{ + // Allocate memory for element. + cell *e = calloc(1, sizeof(*e)); + // Set element value. + e->val = v; + // Link to current top. + e->next = s->top; + // Put element on top of stack. + s->top = e; + // Return modified stack. + return s; +} + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_pop(stack *s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_pop: Warning: pop on empty stack\n"); + } else { + // Remember top element. + cell *e = s->top; + // Link past top element. + s->top = s->top->next; + // De-allocate user memory. + if (s->kill_func != NULL) { + s->kill_func(e->val); + } + // De-allocate element memory. + free(e); + } + return s; +} + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +void *stack_top(const stack *s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_top: Warning: top on empty stack\n"); + } + return s->top->val; +} + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Return all dynamic memory used by the stack and its elements. If a + * kill_func was registered at stack creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void stack_kill(stack *s) +{ + while (!stack_is_empty(s)) { + s = stack_pop(s); + } + free(s); +} + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * @print_func: Function called for each element. + * + * Iterates over the stack and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void stack_print(const stack *s, inspect_callback print_func) +{ + printf("{ "); + cell *e = s->top; + while (e != NULL) { + print_func(e->val); + e = e->next; + if (e != NULL) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the stack head struct. + * @indent_level: Indentation level. + * @s: Stack to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const stack *s) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"kill\\n%04lx|top\\n%04lx\"]\n", + PTR2ADDR(s), PTR2ADDR(s->kill_func), PTR2ADDR(s->top)); +} + +// Print edges from the stack head to the head cell. +static void print_head_edge(int indent_level, const stack *s) +{ + print_edge(indent_level, s, s->top, "t", "top", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const cell *p) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"val\\n%04lx|next\\n%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the stack owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const stack *s, const cell *p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + + if (s->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * stack_print_internal() - Print the stacks internal structure in dot format. + * @l: Stack to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the stack and outputs dot code that shows the internal + * structure of the stack. The dot code can be visualized by Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph STACK_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "s [label=\"%04lx\" xlabel=\"s\"]\n", PTR2ADDR(s)); + iprintf(il, "s -> m%04lx\n", PTR2ADDR(s)); + } + + // Print the subgraph to surround the Stack content + iprintf(il, "subgraph cluster_stack_%d { label=\"Stack\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, s); + + // Output the element nodes + cell *p = s->top; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = s->top; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, s); + + // Output the edges from each element + p = s->top; + while (p != NULL) { + print_elem_edges(il, s, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/stack/stack_mwe1.c b/OU3/src/stack/stack_mwe1.c new file mode 100644 index 0000000..eba1d03 --- /dev/null +++ b/OU3/src/stack/stack_mwe1.c @@ -0,0 +1,89 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer stack without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. + stack *s = stack_empty(NULL); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("--STACK before popping--\n"); + stack_print(s, print_int); + + // Get value on top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + + printf("--STACK after popping--\n"); + stack_print(s, print_int); + + // Now we must empty the stack and free each value ourselves. + while (!stack_is_empty(s)) { + // Get value from top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + } + // Finally, destroy the bare stack. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/stack/stack_mwe1i.c b/OU3/src/stack/stack_mwe1i.c new file mode 100644 index 0000000..c1968a8 --- /dev/null +++ b/OU3/src/stack/stack_mwe1i.c @@ -0,0 +1,133 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c that shows how the internal + * structure of a stack can be visualized. In this version, the stack + * "borrows" the payload memory, i.e., the user of the stack is + * responsible for deallocating the payload memory. See stack_mwe2i.c + * for a version where the stack takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call stack_print_internal. +void print_internal_with_cut_lines(const stack *s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer stack without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack *s = stack_empty(NULL); + + printf("Empty stack from the outside:\n"); + stack_print(s, print_int); + print_internal_with_cut_lines(s, "Empty stack showing the internal structure"); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("Stack from the outside after pushing 3 values:\n"); + stack_print(s, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Stack after pushing 3 values.\n" + "Red lines are used for the stack payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the stack, i.e., the payload\n" + "memory will NOT be deallocated by the stack.\n\n" + "See stack_mwe2i for a stack example\nthat owns the payload memory."; + print_internal_with_cut_lines(s, long_desc); + + // Now we must empty the stack and free each value ourselves. + while (!stack_is_empty(s)) { + // Get value from top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + } + // Finally, destroy the bare stack. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/stack/stack_mwe2.c b/OU3/src/stack/stack_mwe2.c new file mode 100644 index 0000000..fe4f01c --- /dev/null +++ b/OU3/src/stack/stack_mwe2.c @@ -0,0 +1,78 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2022-03-24: Update to always take care of returned pointer. + * v1.3 2023-01-14: Added printouts at start/end of main. + * v1.4 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.4" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer stack with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. Make the stack responsible for + // deallocation pushed values. + stack *s = stack_empty(int_kill); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("--STACK before popping--\n"); + stack_print(s, print_int); + + // Pop'ing will automatically deallocate the payload + s = stack_pop(s); + + printf("--STACK after popping--\n"); + stack_print(s, print_int); + + // Killing will automatically deallocate the payload + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/stack/stack_mwe2i.c b/OU3/src/stack/stack_mwe2i.c new file mode 100644 index 0000000..b1d63bc --- /dev/null +++ b/OU3/src/stack/stack_mwe2i.c @@ -0,0 +1,125 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c that shows how the internal + * structure of a stack can be visualized. In this version, the stack + * "owns" the payload memory, i.e., the stack takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See stack_mwe1i.c for a version + * where the stack does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call stack_print_internal. +void print_internal_with_cut_lines(const stack *s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer stack with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack *s = stack_empty(int_kill); + + printf("Empty stack from the outside:\n"); + stack_print(s, print_int); + print_internal_with_cut_lines(s, "Empty stack showing the internal structure"); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("Stack from the outside after pushing 3 values:\n"); + stack_print(s, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Stack after pushing 3 values.\n" + "Red lines are used for the stack payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the stack, i.e., the payload\n" + "memory WILL be deallocated by the stack.\n\n" + "See stack_mwe1i for a stack example\nthat borrows the payload memory."; + print_internal_with_cut_lines(s, long_desc); + + // Kill the stack. The payload memory is returned automatically. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/table/Makefile b/OU3/src/table/Makefile new file mode 100644 index 0000000..e46509a --- /dev/null +++ b/OU3/src/table/Makefile @@ -0,0 +1,68 @@ +MWE = table_mwe1 table_mwe2 table_mwe3 table2_mwe1 table2_mwe2 table2_mwe3 table_mwe1i table_mwe2i table2_mwe1i table2_mwe2i + +SRC = table.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +table_mwe1: table_mwe1.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe2: table_mwe2.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe3: table_mwe3.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe1i: table_mwe1i.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe2i: table_mwe2i.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe1: table_mwe1.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe2: table_mwe2.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe3: table_mwe3.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe1i: table_mwe1i.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe2i: table_mwe2i.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +memtest11: table_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest12: table_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest13: table_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest21: table2_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest22: table2_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest23: table2_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + diff --git a/OU3/src/table/README.md b/OU3/src/table/README.md new file mode 100644 index 0000000..752c86f --- /dev/null +++ b/OU3/src/table/README.md @@ -0,0 +1,125 @@ +# Tabellimplementation + +Det som skiljer tabellen från de övriga datatyperna är att vi här måste hantera +både nycklar och värden. Denna hantering fungerar analogt med de övriga, men vi +konkretiserar ändå med ett par exempel på hur tabellen kan användas. + +## Jämförelsefunktion + +För att vår tabell ska fungera behöver vi specificera hur nycklar ska jämföras. +Detta tillåter oss att t.ex. använda strängar eller heltal som nycklar utan att +den underliggande implementationen behöver ändras. Detta bygger på en +`compare_function`, som tar två pekare och returnerar `0` ifall de är lika, `0>` +om första värdet är större än det andra, samt `0<` om första värdet är mindre än +det andra. I fallet med heltal kan detta se ut såhär: + +```c +static int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} +``` + +Vi kan då skapa en tabell med `table_empty(compare_ints, free, free) som vi +sedan kan använda såhär: + +```c +table *t = table_empty(compare_ints, free, free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); + +int key_to_lookup = 5; +table_lookup(t, &key_to_lookup); +``` + +Om vår `compare_function` är felaktigt implementerad så kommer inte +`table_lookup` hitta värdet, trots att vi stoppat in det. + +## Minneshantering + +När vi skapar en tabell måste vi specificera hur minnet för nycklar och värden +ska hanteras. Ofta kommer vi vilja allokera minne på heapen dynamiskt, och låta +tabellen avallokera både nyklar och värden: + +```c +table *t = table_empty(compare_ints, free, free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); +table_kill(t); +``` + +Vi skulle även kunna låta nycklarna ligga på stacken, och bara allokera värden +på heapen enl. följande exempel: + +```c +table *t = table_empty(compare_ints, NULL, free); + +int key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, &key, value); +table_kill(t); +``` + +Det skulle också kunna vara så att nycklarna allokeras dynamiskt, men att de +också stoppas i en annan datatyp (t.ex. en lista). Följande kodsnutt illustrera +det exemplet: + +```c +table *t = table_empty(compare_ints, NULL, free); +list *l = list_empty(free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); +list_insert(l, key, list_first(l)); + +// Kommer inte frigöra key, bara value +table_kill(t); + +// Frigör också key +list_kill(l); +``` + +## Utskrift + +Till skillnad från de övriga datatyperna så vill vi skriva ut både nyckeln och +värdet med vår print-funktion. Istället för att exponera den interna `struct`:en +`table_entry` utanför c-filen, så använder vi oss istället av en print-funktion +som tar två parameterar. + +```c +static void print_int_string_pair(const void *key, const void *value) +{ + printf("[%d, %s]\n", *(int*)key, (char*)value); +} +``` + +Ett anrop till `table_print(t, print_int_string_pair)` skulle då skriva ut `[5, + test]` om vi utgår ifrån det tidigare exemplet. + + +# Minimal Working Example + +Se [table_mwe1.c](table_mwe1.c), [table_mwe2.c](table_mwe2.c) och [table_mwe3.c](table_mwe3.c). diff --git a/OU3/src/table/table.c b/OU3/src/table/table.c new file mode 100644 index 0000000..926e916 --- /dev/null +++ b/OU3/src/table/table.c @@ -0,0 +1,687 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2019-03-04: Bugfix in table_remove. + * v1.2 2024-04-15: Added table_print_internal. + * v2.0 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(table_entry_kill); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + // Iterate over the list. Return first match. + + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + // If yes, return the corresponding value pointer. + return e->value; + } + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/table/table2.c b/OU3/src/table/table2.c new file mode 100644 index 0000000..c073ee5 --- /dev/null +++ b/OU3/src/table/table2.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2019-02-21: Second version without dlist/memfreehandler. + * v1.2 2019-03-04: Bugfix in table_remove. + * v1.3 2024-04-15: Added table_print_internal. + * v2.0 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(NULL); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + // Iterate over the list. Return first match. + + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + // If yes, return the corresponding value pointer. + return e->value; + } + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU3/src/table/table_mwe1.c b/OU3/src/table/table_mwe1.c new file mode 100644 index 0000000..5b7dac5 --- /dev/null +++ b/OU3/src/table/table_mwe1.c @@ -0,0 +1,135 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Makes two lookups and prints the + * result. The responsibility to deallocate the key-value pairs is NOT + * handed over to the table. Thus, all pointers must be stored outside + * the table. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.01 2019-03-05: Improved docs. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Keep track of the key-value pairs we allocate. + int *key[4]; + char *value[4]; + + // Keep the deallocation responsibility of the keys/values we allocate. + table *t = table_empty(compare_ints, NULL, NULL); + + key[0] = int_create(90187); + value[0] = string_copy("Umea"); + table_insert(t, key[0], value[0]); + + key[1] = int_create(90184); + value[1] = string_copy("Umea"); + table_insert(t, key[1], value[1]); + + key[2] = int_create(98185); + value[2] = string_copy("Kiruna"); + table_insert(t, key[2], value[2]); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + key[3] = int_create(90187); + value[3] = string_copy("Umea (Universitet)"); + table_insert(t, key[3], value[3]); + + printf("Table after adding a duplicate:\n"); + table_print(t, print_int_string_pair); + + v = 90187; + s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Kill the table, excluding the keys and values we entered. + table_kill(t); + + // Free key/value pairs that we put in the table. + for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) { + int_kill(key[i]); + string_kill(value[i]); + } + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/table/table_mwe1i.c b/OU3/src/table/table_mwe1i.c new file mode 100644 index 0000000..01e4bcb --- /dev/null +++ b/OU3/src/table/table_mwe1i.c @@ -0,0 +1,173 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Output dot/graphviz code to show + * the internal structure of the table. In this version, the table + * "borrows" the payload memory, i.e., the user of the table is + * responsible for deallocating the payload memory. See table_mwe2i.c + * for a version where the table takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-11: First public version. Adapted from table_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_string_int_pair(const void *key, const void *value) +{ + const char *k=key; + const int *v=value; + printf("[%s, %d]\n", k, *v); +} + +// Interpret the supplied key pointer and print its content. +void print_int(const void *key) +{ + const int *k=key; + printf("%d", *k); +} + +// Interpret the supplied value pointer and print its content. +void print_string(const void *value) +{ + const char *v=value; + printf("\\\"%s\\\"", v); +} + +// Compare two keys (char *). +int compare_strings(const void *k1, const void *k2) +{ + const char *key1 = k1; + const char *key2 = k2; + + return strcmp(key1, key2); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const table *t, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + table_print_internal(t, print_string, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Keep track of the key-value pairs we allocate. + char *key[3]; + int *value[3]; + + // Keep the deallocation responsibility of the keys/values we allocate. + table *t = table_empty(compare_strings, NULL, NULL); + + printf("Empty table from the outside:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Empty table showing the inside structure."); + + key[0] = string_copy("Jan"); + value[0] = int_create(31); + table_insert(t, key[0], value[0]); + + key[1] = string_copy("Feb"); + value[1] = int_create(28); + table_insert(t, key[1], value[1]); + + key[2] = string_copy("Mar"); + value[2] = int_create(31); + table_insert(t, key[2], value[2]); + + printf("Table from the outside after adding 3 key-value pairs:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Table with 3 key-value pairs showing the inside structure."); + + // Kill the table, excluding the keys and values we entered. + table_kill(t); + + // Free key/value pairs that we put in the table. + for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) { + string_kill(key[i]); + int_kill(value[i]); + } + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/table/table_mwe2.c b/OU3/src/table/table_mwe2.c new file mode 100644 index 0000000..6dd42eb --- /dev/null +++ b/OU3/src/table/table_mwe2.c @@ -0,0 +1,130 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Makes two lookups and prints the + * result. The responsibility to deallocate the key-value pairs IS + * handed over to the table. Thus, no key-value pointers need to be + * stored outside the table. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.01 2019-03-05: Improved docs. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table with kill_functions.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Delegate the deallocation responsibility to the table. + table *t = table_empty(compare_ints, int_kill, string_kill); + + // We don't need to store the keys and values outside the + // table since the deallocation will be done by the table. + int *key; + char *value; + + key = int_create(90187); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(90184); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(98185); + value = string_copy("Kiruna"); + table_insert(t, key, value); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s=table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + key = int_create(90187); + value = string_copy("Umea (Universitet)"); + table_insert(t, key, value); + + printf("Table after adding a duplicate:\n"); + table_print(t, print_int_string_pair); + + v = 90187; + s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Kill the table, including the keys and values we entered. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/table/table_mwe2i.c b/OU3/src/table/table_mwe2i.c new file mode 100644 index 0000000..1ceb2a0 --- /dev/null +++ b/OU3/src/table/table_mwe2i.c @@ -0,0 +1,167 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Output dot/graphviz code to show + * the internal structure of the table. In this version, the table + * "owns" the payload memory, i.e., the table is responsible for + * deallocating the payload memory. See table_mwe1i.c for a version + * where the table borrows the memory. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-11: First public version. Adapted from table_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_string_int_pair(const void *key, const void *value) +{ + const char *k=key; + const int *v=value; + printf("[%s, %d]\n", k, *v); +} + +// Interpret the supplied key pointer and print its content. +void print_int(const void *key) +{ + const int *k=key; + printf("%d", *k); +} + +// Interpret the supplied value pointer and print its content. +void print_string(const void *value) +{ + const char *v=value; + printf("\\\"%s\\\"", v); +} + +// Compare two keys (char *). +int compare_strings(const void *k1, const void *k2) +{ + const char *key1 = k1; + const char *key2 = k2; + + return strcmp(key1, key2); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const table *t, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + table_print_internal(t, print_string, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Keep track of the key-value pairs we allocate. + char *key[3]; + int *value[3]; + + // Delegate the deallocation responsibility to the table. + table *t = table_empty(compare_strings, string_kill, int_kill); + + printf("Empty table from the outside:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, "Empty table showing the inside structure."); + + key[0] = string_copy("Jan"); + value[0] = int_create(31); + table_insert(t, key[0], value[0]); + + key[1] = string_copy("Feb"); + value[1] = int_create(28); + table_insert(t, key[1], value[1]); + + key[2] = string_copy("Mar"); + value[2] = int_create(31); + table_insert(t, key[2], value[2]); + + printf("Table from the outside after adding 3 key-value pairs:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Table with 3 key-value pairs showing the inside structure."); + + // Kill the table, including the keys and values we entered. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/table/table_mwe3.c b/OU3/src/table/table_mwe3.c new file mode 100644 index 0000000..f12d9c1 --- /dev/null +++ b/OU3/src/table/table_mwe3.c @@ -0,0 +1,129 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 3 key-value pairs into + * a table, no duplicates. Makes one lookup and prints the result. The + * responsibility to deallocate the key-value pairs is NOT handed over + * to the table. Uses table_choose_key() to extract keys and values to + * be able to destroy the table without memory leaks or externally + * stored pointers. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function " + "nor externally stored pointers, .\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Keep the deallocation responsibility. + table *t = table_empty(compare_ints, NULL, NULL); + + int *key; + char *value; + + key = int_create(90187); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(90184); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(98185); + value = string_copy("Kiruna"); + table_insert(t, key, value); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s=table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Simple cleanup that works if the table is without duplicates. + while (!table_is_empty(t)) { + // Get one key from the table. + key = table_choose_key(t); + // Lookup the corresponding value. + value = table_lookup(t, key); + // Remove the key-value pair from the table. + table_remove(t, key); + // De-allocate key and value. + int_kill(key); + string_kill(value); + } + // Kill what is left by the table. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU3/src/version/version.c b/OU3/src/version/version.c new file mode 100644 index 0000000..ac91937 --- /dev/null +++ b/OU3/src/version/version.c @@ -0,0 +1,26 @@ +/* + * File to hold library version information for the code base for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2023-01-14: First public version. + */ + +#include + +// Create a struct with all version information to be stored in the +// library file. +struct { + const char* long_version; + const char* version; + const unsigned major; + const unsigned minor; + const unsigned revision; + const unsigned patch; +} code_base_version = { + CODE_BASE_LONG_VERSION, CODE_BASE_VERSION, CODE_BASE_MAJOR_VERSION, + CODE_BASE_MINOR_VERSION, CODE_BASE_REVISION, CODE_BASE_PATCH +}; diff --git a/OU3/tableprint.c b/OU3/tableprint.c new file mode 100644 index 0000000..c5920c9 --- /dev/null +++ b/OU3/tableprint.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +int key_cmp_func(const void *a, const void *b) +{ + + const int *pntkey1 = a; + const int key1 = *pntkey1; + const int *pntkey2 = b; + const int key2 = *pntkey2; + + if (key1 == key2) + return 0; + if (key1 < key2) + return -1; + return 1; +} + +// int *print_func(table *t) + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int(const void *key,const void *value) +{ + const int *k = key; + const int *s = value; + printf("[%d, %d]\n", *k, *s); +} + +int main(void) +{ + + printf("================ TABLETESTER ================\n"); + table *t = table_empty(key_cmp_func, int_kill, int_kill); + + + + + + /**/ + printf("Empty table created...\n"); + + int choice = 1; + while (choice != 0) + { + printf("Choose Next Action!\n[0} Exit\n[1] table_is_empty\n[2] table_insert\n"); + printf("[3] table_lookup\n[4] table_remove\n[5] table_print\n"); + scanf("%d",&choice); + printf("\nChoice[%d]\n\n",choice); + if (choice == 1) + { + bool empty = table_is_empty(t); + if(empty) + { + printf("Table is empty...\n"); + } + else + { + printf("Table is not empty...\n"); + } + + } + if (choice == 2) + { + printf("table_insert()\nKey..."); + int key; + scanf("%d", &key); + printf("\nValue..."); + int value; + scanf("%d", &value); + table_insert(t, int_create(key), int_create(value)); + printf("\nInsert Complete!\n\n"); + } + if (choice == 3) + { + printf("table_lookup()\nKey..."); + int key; + scanf("%d", &key); + int *pntr = table_lookup(t, int_create(key)); + //int val = *pntr; + printf("\nLookup complete...[%d]\n\n", *pntr); + } + if (choice == 4) + { + printf("table_remove()\nKey..."); + int key; + scanf("%d", &key); + table_remove(t, int_create(key)); + printf("\nRemoval complete...\n\n"); + } + if (choice == 5) + { + table_print(t, print_int); + } + if (choice > 5) + { + printf("invalid choice...\n\n"); + } + } + + + return 0; +} \ No newline at end of file diff --git a/OU3/tabletest b/OU3/tabletest new file mode 100755 index 0000000..35847f1 Binary files /dev/null and b/OU3/tabletest differ diff --git a/OU3/tabletest.c b/OU3/tabletest.c new file mode 100644 index 0000000..f298264 --- /dev/null +++ b/OU3/tabletest.c @@ -0,0 +1,800 @@ +/* + * tabletest - test different implementation of a table. + * + * Should be compiled together with a table implementation that + * follows the interface in table.h of the code base for the + * Datastructures and Algorithms courses at the Department of + * Computing Science, Umea University. + * + * 20xx-xx-xx v1.0 Lena Kallin Westin . + * 2017-03-xx v1.1 Adam Dahlstrom + * Modified to use dynamic memory. + * 2017-05-xx v1.2 Niclas Borlin . + * Simplified printout and use. Test size can now be + * specified on the command line. Lookup (no key) moved + * before lookup to avoid cache-hit effects on timing of + * mtftable. + * 2017-05-xx v1.3 Niclas Borlin . + * Added more fine-grained tests. + * 2018-02-08 v1.4 Niclas Borlin . + * Modified to match 2018 implementation of the data + * structure code base. + * 2018-02-20 v1.5 Niclas Borlin . + * Now completely destroys and rebuilds table between timed + * tests to reduce cache effects. + * 2018-04-12 v1.6 Niclas Borlin . + * Modified program to accept two parameters; number of + * elements and number of lookups. + * 2018-05-02 v1.7 Niclas Borlin . + * Bugfix in get_skewed_lookup_speed. + * 2019-02-12 v1.8 Reverted back to single-parameter version v1.6. + * 2019-04-17 v1.9 Added -m for machine-readable results. + * 2023-02-17 v1.10 Added printout of the codebase version. +*/ + +#define VERSION "v1.10" +#define VERSION_DATE "2023-02-17" + +/* + * Correctness testing algorithm: + * + * 1. Tests if isempty returns true directly after a table is created + * 2. Tests if isempty returns false directly after a table is created + * and one element (key-value-pair) is inserted to it. + * 3. Tests a table by creating it and inserting one key-value-pair. After + * that it is checked that the returned values from a lookup are the ones + * expected. First by looking up a non-existent key, then by looking up + * an existent key. + * 4. Tests a table by creating it and inserting three key-value-pairs with + * unique keys. After that, a lookup for all three keys are tested and + * it is checked that the returned values are the ones expected. + * 5. Tests a table by creating it and inserting three key-value-pairs with + * the same keys. After that, a lookup for the key is tested and it is + * checked that the returned value is the last one inserted to the table. + * 6. Tests a table by creating it and inserting one key-value-pair. After + * that the element is removed and it is checked that the table is empty. + * 7. Tests a table by creating it and inserting three key-value-pairs. After + * that the elements is removed one at a time and it is checked that the + * table is empty after the third element is removed. + * 8. Tests a table by creating it and inserting a single key-value + * pair followed by three key-value-pairs with identical + * keys. After that, the first element is remove and it is verified + * that it is gone and that the other key returns the cocorrect + * value. The second key is removed and it is checked that the + * table is empty. + * + * There is also a module measuring time for insertions, lookups etc. + * */ +#include +#include +#include +#include +#include +#include "table.h" + +// Maximum size of the table to generate +#define TABLESIZE 40000 +#define SAMPLESIZE TABLESIZE*2 + +/** + * copy_string() - Create a dynamic copy of a string. + * @s: String to be copied. + * + * Allocates memory for a dynamic copy of s and copies the contents of + * s into the copy. + * + * Returns: Pointer to the copy of s. + */ +char *copy_string(const char *s) +{ + int len=strlen(s); + + /* Allocate memory for new string, with an extra char for \0 */ + char *dest = malloc(sizeof(char)*(len+1)); + + /* Malloc failed, return NULL */ + if (dest == NULL) { + return NULL; + } + + /* Copy content to new memory */ + strncpy(dest, s, len); + + /* Strings should always be null terminated */ + dest[len] = '\0'; + return dest; +} + +/** + * int_ptr_from_int() - Create a dynamic copy of an integer. + * @i: String to be copied. + * + * Allocates memory for a dynamic copy of i and copies the contents of + * i into the copy. + * + * Returns: Pointer to the copy of i. + */ +int *int_ptr_from_int(int i) +{ + // Allocate memory for a dynamic copy of an integer + int *ip=malloc(sizeof(int)); + // Copy the value + *ip=i; + return ip; +} + +/** + * get_milliseconds() - Return the current time-of-day in milliseconds. + * + * Returns: The current time-of-day in milliseconds. + */ +unsigned long get_milliseconds() +{ + struct timeval tv; + gettimeofday(&tv, 0); + return (unsigned long)(tv.tv_sec*1000 + tv.tv_usec/1000); +} + +/** + * int_compare() - Compare to integers via pointers. + * @ip1, @ip2: Pointers to integers to be compared. + * + * Compares the integers that ip1 and ip2 points to. + * + * Returns: 0 if the integers are equal, negative if the first + * argument is smaller, positive if the first argument is larger. + */ +int int_compare(const void *ip1,const void *ip2) +{ + const int *n1=ip1; + const int *n2=ip2; + return (*n1 - *n2); +} + +/** + * string_compare() - Compare two strings. + * @ip1, @ip2: Pointers to strings to be compared. + * + * Compares the strings that ip1 and ip2 points to. + * + * Returns: 0 if the strings are equal, negative if the first + * argument is smaller, positive if the first argument is larger. + */ +int string_compare(const void *ip1,const void *ip2) +{ + const char *s1=ip1; + const char *s2=ip2; + return strcmp(s1,s2); +} + +/* Shuffles the numbers stored in seq + * seq - an array of randomnumbers to be shuffled + * n - the number of elements in seq to shuffle, i.e the indexes [0, n] + * will be shuffled. However, seq might be larger than n... + */ +void random_shuffle(int seq[], int n) +{ + for(int i=0;i0 && s[0]=='-') { + switch (s[1]) { + case 'n': + do_test=false; + break; + case 't': + machine_table=true; + break; + default: + fprintf(stderr,"%s: Bad switch: %s.\n", + argv[0],s); + exit(EXIT_FAILURE); + } + } else { + // Convert string to integer. + n=atoi(s); + break; + } + } + + if (n<0) { + fprintf(stderr,"Usage:\n\t%s [-n] [-t] n\n" + "\twhere n is an integer from 1 to %d.\n\n" + "\tUse -n (no-test) to skip the testing.\n" + "\tUse -t (table) to output a machine-readable table with the timings.\n", + argv[0],TABLESIZE); + exit(EXIT_FAILURE); + } + if (n<1 || n>TABLESIZE) { + fprintf(stderr,"Error: supplied value of n (%d) is outside " + "allowed range 1-%d.\n",n,TABLESIZE); + exit(EXIT_FAILURE); + } + if (do_test) { + printf("Testing...\n"); + correctness_test(); + printf("All correctness tests succeeded!\n\n"); + } + /*getchar();*/ + speed_test(n,machine_table); + if (!machine_table) { + printf("Test completed.\n"); + } + return 0; +} diff --git a/OU3/timeit.sh b/OU3/timeit.sh new file mode 100644 index 0000000..bef5ff6 --- /dev/null +++ b/OU3/timeit.sh @@ -0,0 +1,25 @@ +rm ttiming.txt mtiming.txt atiming.txt +touch ttiming.txt mtiming.txt atiming.txt + +for i in 10000 13000 16000 19000 22000; +do + echo + echo Running sample size $i... + for j in 1 2 3 4 5 6; + do + echo Running tabletest $j/6... + ./tabletest -t -n $i >> ttiming.txt + done + + for j in 1 2 3 4 5 6; + do + echo Running mtftabletest $j/6... + ./mtftabletest -t -n $i >> mtiming.txt + done + + for j in 1 2 3 4 5 6; + do + echo Running arraytabletest $j/6... + ./arraytabletest -t -n $i >> atiming.txt + done +done diff --git a/OU3/ttiming.txt b/OU3/ttiming.txt new file mode 100644 index 0000000..55be394 --- /dev/null +++ b/OU3/ttiming.txt @@ -0,0 +1,150 @@ +1, 10000, 2 +2, 10000, 525 +3, 10000, 1619 +4, 10000, 851 +5, 10000, 871 +1, 10000, 1 +2, 10000, 523 +3, 10000, 1619 +4, 10000, 853 +5, 10000, 874 +1, 10000, 1 +2, 10000, 532 +3, 10000, 1622 +4, 10000, 856 +5, 10000, 878 +1, 10000, 1 +2, 10000, 526 +3, 10000, 1621 +4, 10000, 839 +5, 10000, 875 +1, 10000, 1 +2, 10000, 532 +3, 10000, 1631 +4, 10000, 855 +5, 10000, 884 +1, 10000, 2 +2, 10000, 528 +3, 10000, 1631 +4, 10000, 840 +5, 10000, 871 +1, 13000, 2 +2, 13000, 947 +3, 13000, 3018 +4, 13000, 1605 +5, 13000, 1606 +1, 13000, 1 +2, 13000, 917 +3, 13000, 2988 +4, 13000, 1592 +5, 13000, 1630 +1, 13000, 1 +2, 13000, 926 +3, 13000, 3016 +4, 13000, 1594 +5, 13000, 1614 +1, 13000, 1 +2, 13000, 913 +3, 13000, 2999 +4, 13000, 1579 +5, 13000, 1633 +1, 13000, 1 +2, 13000, 918 +3, 13000, 2990 +4, 13000, 1618 +5, 13000, 1647 +1, 13000, 1 +2, 13000, 932 +3, 13000, 2944 +4, 13000, 1539 +5, 13000, 1566 +1, 16000, 2 +2, 16000, 1412 +3, 16000, 4578 +4, 16000, 2533 +5, 16000, 2560 +1, 16000, 1 +2, 16000, 1448 +3, 16000, 4552 +4, 16000, 2518 +5, 16000, 2528 +1, 16000, 2 +2, 16000, 1395 +3, 16000, 4467 +4, 16000, 2425 +5, 16000, 2495 +1, 16000, 2 +2, 16000, 1427 +3, 16000, 4486 +4, 16000, 2425 +5, 16000, 2535 +1, 16000, 2 +2, 16000, 1390 +3, 16000, 4486 +4, 16000, 2510 +5, 16000, 2565 +1, 16000, 3 +2, 16000, 1444 +3, 16000, 4514 +4, 16000, 2468 +5, 16000, 2507 +1, 19000, 3 +2, 19000, 1988 +3, 19000, 6567 +4, 19000, 3624 +5, 19000, 3632 +1, 19000, 2 +2, 19000, 2026 +3, 19000, 6599 +4, 19000, 3596 +5, 19000, 3591 +1, 19000, 2 +2, 19000, 1961 +3, 19000, 6644 +4, 19000, 3628 +5, 19000, 3572 +1, 19000, 2 +2, 19000, 2046 +3, 19000, 6582 +4, 19000, 3662 +5, 19000, 3648 +1, 19000, 2 +2, 19000, 2089 +3, 19000, 6739 +4, 19000, 3618 +5, 19000, 3714 +1, 19000, 2 +2, 19000, 2090 +3, 19000, 6699 +4, 19000, 3597 +5, 19000, 3612 +1, 22000, 3 +2, 22000, 2669 +3, 22000, 8756 +4, 22000, 4892 +5, 22000, 4918 +1, 22000, 3 +2, 22000, 2725 +3, 22000, 8823 +4, 22000, 4839 +5, 22000, 4980 +1, 22000, 2 +2, 22000, 2709 +3, 22000, 9002 +4, 22000, 4809 +5, 22000, 4962 +1, 22000, 2 +2, 22000, 2752 +3, 22000, 8950 +4, 22000, 4805 +5, 22000, 4910 +1, 22000, 2 +2, 22000, 2762 +3, 22000, 8931 +4, 22000, 4818 +5, 22000, 4919 +1, 22000, 3 +2, 22000, 2752 +3, 22000, 8772 +4, 22000, 4781 +5, 22000, 4855 diff --git a/OU3/vgcore.80389 b/OU3/vgcore.80389 new file mode 100644 index 0000000..8f036a1 Binary files /dev/null and b/OU3/vgcore.80389 differ diff --git a/OU3/vgcore.80560 b/OU3/vgcore.80560 new file mode 100644 index 0000000..7531415 Binary files /dev/null and b/OU3/vgcore.80560 differ diff --git a/OU3/vgcore.80686 b/OU3/vgcore.80686 new file mode 100644 index 0000000..ac1f722 Binary files /dev/null and b/OU3/vgcore.80686 differ diff --git a/OU3/vgcore.80967 b/OU3/vgcore.80967 new file mode 100644 index 0000000..7254f93 Binary files /dev/null and b/OU3/vgcore.80967 differ diff --git a/OU3/vgcore.81134 b/OU3/vgcore.81134 new file mode 100644 index 0000000..fa89e38 Binary files /dev/null and b/OU3/vgcore.81134 differ diff --git a/OU3/vgcore.81196 b/OU3/vgcore.81196 new file mode 100644 index 0000000..e84f156 Binary files /dev/null and b/OU3/vgcore.81196 differ diff --git a/OU4/.vscode/settings.json b/OU4/.vscode/settings.json new file mode 100644 index 0000000..2d62a43 --- /dev/null +++ b/OU4/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "files.associations": { + "array": "c", + "chrono": "c", + "deque": "c", + "string": "c", + "unordered_map": "c", + "vector": "c", + "iterator": "c", + "string_view": "c", + "format": "c", + "span": "c" + } +} \ No newline at end of file diff --git a/OU4/airmap1.map b/OU4/airmap1.map new file mode 100644 index 0000000..2781ef8 --- /dev/null +++ b/OU4/airmap1.map @@ -0,0 +1,10 @@ +# Some airline network +896.7 +BMA MMX #hrjraiwefguil +#BMA UME # Bromma-Umea +1BMA MMX # Bromma-Malmo +1MMX BMA # Malmo-Bromma +1BMA GOT # Bromma-Goteborg +1GOT BMA # Goteborg-Bromma +1LLA PJA # Lulea-Pajala +1PJA LLA # Pajala-Lulea diff --git a/OU4/changes.txt b/OU4/changes.txt new file mode 100644 index 0000000..bc9bdf2 --- /dev/null +++ b/OU4/changes.txt @@ -0,0 +1,3 @@ +Inlämning 2: Saknades en reset av seen status efter att "no path found" printades, vilket orsakade att nästa testade nodpar där path finns kunde returnera "no path" ifall pathen gick igenom redan sedda noder. + +Inlämning 3: Vi fixade så att den ger felmedelanden vid felaktig kartfil. diff --git a/OU4/compile.sh b/OU4/compile.sh new file mode 100644 index 0000000..0c24b7e --- /dev/null +++ b/OU4/compile.sh @@ -0,0 +1,7 @@ +#!/bin/bash +#gcc -Wall -I include/ -g -o graph2-marctest marctest.c graph2.c src/dlist/dlist.c +gcc -Wall -std=c99 -I include/ -g -o graph2 is_connected.c graph2.c src/dlist/dlist.c src/queue/queue.c src/list/list.c + #table/table2.c + +gcc -Wall -I include/ -g -o graph is_connected.c graph.c src/dlist/dlist.c src/queue/queue.c src/list/list.c src/array_2d/array_2d.c +#gcc -Wall -I include/ -g -o arraytabletest tabletest.c arraytable.c src/array_1d/array_1d.c diff --git a/OU4/graph b/OU4/graph new file mode 100755 index 0000000..3313157 Binary files /dev/null and b/OU4/graph differ diff --git a/OU4/graph.c b/OU4/graph.c new file mode 100644 index 0000000..6455c47 --- /dev/null +++ b/OU4/graph.c @@ -0,0 +1,484 @@ +#include +#include // ska innehålla array_2d implementation +#include +#include +#include +#include + +/* + * graph.c: Implementation of a 2-dimensional array based graph for "Obligatorisk uppgift 4" in the "Datastructures and + * algorithms" course at the Department of Computing Science, Umea University. + * + * Authors: Jakob Nyström tfy22jnm + * Ellen Horneij tfy22ehj + * Marc Meunier tfy22mmr + * + * Version information: + * v1.0 2025-05-25: First public version. + * + */ + +// ===========INTERNAL DATA TYPES ============ + +// Graph contains the matrix with nodes and edges, total number of nodes ans edges and +struct graph +{ + array_2d *entries; + int nr_of_nodes; + int nr_of_edges; + int max_nodes; +}; + +/** + * Node entry contains the value of the node, it's position and it's seen status + */ +struct node +{ + const char *value; + int position; + bool seen; +}; + +// =================== NODE COMPARISON FUNCTION ====================== + +/** + * nodes_are_equal() - Check whether two nodes are equal. + * @n1: Pointer to node 1. + * @n2: Pointer to node 2. + * + * Returns: true if the nodes are considered equal, otherwise false. + * + */ +bool nodes_are_equal(const node *n1, const node *n2) +{ + // Uses strcomp to compare the nodes + int cmp = strcmp(n1->value, n2->value); + + // Returns true if strcomp returns 0 meaning that the nodes are equal, if else false + if (cmp == 0) + { + return true; + } + else + { + return false; + } +} +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * node_create() - Allocates the memory and creates a node entry + * @s: the value of the node + * + * Returns: The pointer to node entry with value s + */ +node *node_create(const char *s) +{ + // Allocate the memory for the node entry + node *n = calloc(1, sizeof(*n)); + + // Populate the entry. + n->seen = false; + + char *tmp = malloc(strlen(s) + 1); + if (tmp != NULL) + { + strcpy(tmp, s); + n->value = tmp; + } + else + { + // Handle memory allocation failure + free(n); + return NULL; + } + return n; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void node_kill(node *v) +{ + //node *n = v; // Convert the pointer (useful if debugging the code) + if (v == NULL) { + return; // Nothing to free if v is NULL + } + + // free strings + free((void*)v->value); + + // All we need to do is to deallocate the struct. + free(v); +} + +// =================== GRAPH STRUCTURE INTERFACE ====================== + +/** + * graph_empty() - Create an empty graph. + * @max_nodes: The maximum number of nodes the graph can hold. + * + * Returns: A pointer to the new graph + */ + +graph *graph_empty(int max_nodes) +{ + // Allocate the graph memory + graph *g = calloc(1, sizeof(graph)); + + // Create the array for the entries + g->entries = array_2d_create(0, max_nodes, 0, max_nodes + 1, NULL); + g->nr_of_nodes = 0; + g->nr_of_edges = 0; + g->max_nodes = max_nodes; + return g; +} + +/** + * graph_is_empty() - Check if a graph is empty, i.e. has no nodes. + * @g: Graph to check. + * + * Returns: True if graph is empty, otherwise false. + */ +bool graph_is_empty(const graph *g) +{ + // Returns true if nr_of_nodes is empty + return g->nr_of_nodes == 0; +} + +/** + * graph_has_edges() - Check if a graph has any edges. + * @g: Graph to check. + * + * Returns: True if graph has any edges, otherwise false. + */ +bool graph_has_edges(const graph *g) +{ + // Returns true if nr_of_edges is not empty + return g->nr_of_edges != 0; +} + +/** + * graph_insert_node() - Inserts a node with the given name into the graph. + * @g: Graph to manipulate. + * @s: Node name. + * + * Creates a new node with a copy of the given name and puts it into + * the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_node(graph *g, const char *s) +{ + // Define variables and create a node entry with s for comparisons + int index = 0; + int count = 0; + node *m = node_create(s); + + // Check for duplicates among the already taken slots + while (index < g->max_nodes && count < g->nr_of_nodes) + { + // Inspect the table entry + node *n = array_2d_inspect_value(g->entries, index, 0); + + // Check if the node already exists + if (n != NULL && nodes_are_equal(n, m)) + { + // If the node is a duplicate, exit the loop and skips the insertion loop + node_kill(m); + return g; + } + if (n != NULL) + { + // Update how many taken slots that have been checked + count++; + } + // Continue with the next position. + index++; + } + // If no duplicate is found, insert the node in the first available slot + + while (index < g->max_nodes) + { + // Check for empty slot + node *n = array_2d_inspect_value(g->entries, index, 0); + if (n == NULL) + { + // Input node in the empty slot and update variables before exiting the loop + m->position = index; + m->seen = false; + array_2d_set_value(g->entries, m, index, 0); + g->nr_of_nodes++; + break; + } + index++; + } + + // Return the modified graph + return g; +} + +/** + * graph_find_node() - Find a node stored in the graph. + * @g: Graph to manipulate. + * @s: Node identifier, e.g. a char *. + * + * Returns: A pointer to the found node, or NULL. + */ +node *graph_find_node(const graph *g, const char *s) +{ + int index = 0; + int count = 0; + node *m = node_create(s); + + // Loop over the node list + while (index < g->max_nodes && count < g->nr_of_nodes) + { + // Check if a node with value s exists and return it, if not continue to next node + node *n = array_2d_inspect_value(g->entries, index, 0); + if (n != NULL && nodes_are_equal(n, m)) + { + // Deallocate comparison node + node_kill(m); + return n; + } + if (n != NULL) + { + count++; + } + index++; + } + + // If no match is found, deallocate comparison node and return NULL + node_kill(m); + return NULL; +} + +/** + * graph_node_is_seen() - Return the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to return seen status for. + * + * Returns: The seen status for the node. + */ +bool graph_node_is_seen(const graph *g, const node *n) +{ + // Check if node exists and return its seen-status + node *m = graph_find_node(g, n->value); + if (m != NULL) + { + return m->seen; + } + + // Return NULL if the node is not found + return false; +} + +/** + * graph_node_set_seen() - Set the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to set seen status for. + * @s: Status to set. + * + * Returns: The modified graph. + */ +graph *graph_node_set_seen(graph *g, node *n, bool seen) +{ + // Check if node exists and update its seen-status + node *m = graph_find_node(g, n->value); + if (m != NULL) + { + m->seen = seen; + } + + // Return the modified graph + return g; +} + +/** + * graph_reset_seen() - Reset the seen status on all nodes in the graph. + * @g: Graph to modify. + * + * Returns: The modified graph. + */ +graph *graph_reset_seen(graph *g) +{ + int index = 0; + int count = 0; + + // Iterates over all nodes and sets the seen-status to FALSE + while (index < g->max_nodes && count < g->nr_of_nodes) + { + node *n = array_2d_inspect_value(g->entries, index, 0); + if (n != NULL) + { + n->seen = false; + count++; + } + index++; + } + return g; +} + +/** + * graph_insert_edge() - Insert an edge into the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * NOTE: Undefined unless both nodes are already in the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_edge(graph *g, node *n1, node *n2) +{ + // Checks if both source and destination exists + if (graph_find_node(g, n1->value) != NULL && graph_find_node(g, n2->value) != NULL) + { + // Inserts n2 into the edge list of n1 and updates the nr_of_edges + array_2d_set_value(g->entries, n2, n1->position, n2->position + 1); + g->nr_of_edges++; + } + return g; +} + +/** + * graph_delete_node() - Remove a node from the graph. + * @g: Graph to manipulate. + * @n: Node to remove from the graph. + * + * Returns: The modified graph. + * + * NOTE: Undefined if the node is not in the graph. + */ +graph *graph_delete_node(graph *g, node *n) +{ + // Removes the node and all of its destinations + for (int i = 0; i < g->max_nodes; i++) + { + array_2d_set_value(g->entries, NULL, n->position, 1); + } + + // Updatess the nr_of_nodes and returns the modified graph + g->nr_of_nodes--; + return g; +} + +/** + * graph_delete_edge() - Remove an edge from the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * Returns: The modified graph. + * + * NOTE: Undefined if the edge is not in the graph. + */ +graph *graph_delete_edge(graph *g, node *n1, node *n2) +{ + // Removes n2 from n1's destination list + array_2d_set_value(g->entries, NULL, n1->position, n2->position + 1); + g->nr_of_edges--; + return g; +} + +/** + * graph_neighbours() - Return a list of neighbour nodes. + * @g: Graph to inspect. + * @n: Node to get neighbours for. + * + * Returns: A pointer to a list of nodes. Note: The list must be + * dlist_kill()-ed after use. + */ +dlist *graph_neighbours(const graph *g, const node *n) +{ + // Creates the return list and gets its first position + dlist *l = dlist_empty(NULL); + dlist_pos pos = dlist_first(l); + + if (n != NULL) + { + for (int i = 1; i <= g->max_nodes + 1; i++) + { + // If a destination exists add it to the list of neigbours and update the list position + node *m = array_2d_inspect_value(g->entries, n->position, i); + if (m != NULL) + { + dlist_insert(l, m, pos); + pos = dlist_next(l, pos); + } + } + } + + // Return the list of neigbours + return l; +} + +/** + * graph_kill() - Destroy a given graph. + * @g: Graph to destroy. + * + * Return all dynamic memory used by the graph. + * + * Returns: Nothing. + */ +void graph_kill(graph *g) +{ + // Iterate over the list of nodes and deallocate them + for (int i = 0; i < g->max_nodes; i++) + { + + node *n = array_2d_inspect_value(g->entries, i, 0); + node_kill(n); + } + + // free the array and the graph + array_2d_kill(g->entries); + free(g); +} + +/** + * graph_print() - Iterate over the graph elements and print their values. + * @g: Graph to inspect. + * + * Iterates over the graph and prints its contents. + * + * Returns: Nothing. + */ +void graph_print(const graph *g) +{ + // Itirate over the list and print the value + for (int i = 0; i < g->max_nodes; i++) + { + for (int j = 0; j < g->max_nodes + 1; j++) + { + node *n = array_2d_inspect_value(g->entries, i, j); + + // Check if there is a value to print + if (n != NULL && j == 0) + { + // Print the value + printf("[%s] -> ", n->value); + } + else if (j == 0) + { + // Print no value + printf("[###] -> "); + } + else if (n != NULL) + { + // Print the value + printf("[%s] ", n->value); + } + else + { + // Print no value + printf("[###] "); + } + } + printf("\n"); + } +} diff --git a/OU4/graph.h b/OU4/graph.h new file mode 100644 index 0000000..103190b --- /dev/null +++ b/OU4/graph.h @@ -0,0 +1,200 @@ +#ifndef __GRAPH_H +#define __GRAPH_H + +#include +#include "util.h" +#include "dlist.h" + +/* + * Declaration of a generic graph for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The graph stores nodes and edges of a directed or + * undirected graph. After use, the function graph_kill() must + * be called to de-allocate the dynamic memory used by the graph + * itself. The de-allocation of any dynamic memory allocated for the + * node names is the responsibility of the user of the graph. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se) + * + * Version information: + * v1.0 2019-02-21: First public version. + + * v1.01 2019-02-26: Modified include directives for header files + * from the codebase to use "" to handle case when + * student stores all header files in the current + * directory. See + * https://stackoverflow.com/questions/21593/what-is-the-difference-between-include-filename-and-include-filename. + * v1.02 2019-03-01: Doc update in graph_node_is_seen(). + * v1.1 2019-03-06: Changed several const node * to node *. + * Fixed doc bug to state that any dynamic memory allocated + * to the node NAMES is the resposibility of the user. + */ + +// ====================== PUBLIC DATA TYPES ========================== + +// Anonymous declarations of node and graph. +typedef struct node node; +typedef struct graph graph; + +// =================== NODE COMPARISON FUNCTION ====================== + +/** + * nodes_are_equal() - Check whether two nodes are equal. + * @n1: Pointer to node 1. + * @n2: Pointer to node 2. + * + * Returns: true if the nodes are considered equal, otherwise false. + * + */ +bool nodes_are_equal(const node *n1,const node *n2); + +// =================== GRAPH STRUCTURE INTERFACE ====================== + +/** + * graph_empty() - Create an empty graph. + * @max_nodes: The maximum number of nodes the graph can hold. + * + * Returns: A pointer to the new graph. + */ +graph *graph_empty(int max_nodes); + +/** + * graph_is_empty() - Check if a graph is empty, i.e. has no nodes. + * @g: Graph to check. + * + * Returns: True if graph is empty, otherwise false. + */ +bool graph_is_empty(const graph *g); + +/** + * graph_has_edges() - Check if a graph has any edges. + * @g: Graph to check. + * + * Returns: True if graph has any edges, otherwise false. + */ +bool graph_has_edges(const graph *g); + +/** + * graph_insert_node() - Inserts a node with the given name into the graph. + * @g: Graph to manipulate. + * @s: Node name. + * + * Creates a new node with a copy of the given name and puts it into + * the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_node(graph *g, const char *s); + +/** + * graph_find_node() - Find a node stored in the graph. + * @g: Graph to manipulate. + * @s: Node identifier, e.g. a char *. + * + * Returns: A pointer to the found node, or NULL. + */ +node *graph_find_node(const graph *g, const char *s); + +/** + * graph_node_is_seen() - Return the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to return seen status for. + * + * Returns: The seen status for the node. + */ +bool graph_node_is_seen(const graph *g, const node *n); + +/** + * graph_node_set_seen() - Set the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to set seen status for. + * @s: Status to set. + * + * Returns: The modified graph. + */ +graph *graph_node_set_seen(graph *g, node *n, bool seen); + +/** + * graph_reset_seen() - Reset the seen status on all nodes in the graph. + * @g: Graph to modify. + * + * Returns: The modified graph. + */ +graph *graph_reset_seen(graph *g); + +/** + * graph_insert_edge() - Insert an edge into the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * NOTE: Undefined unless both nodes are already in the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_edge(graph *g, node *n1, node *n2); + +/** + * graph_delete_node() - Remove a node from the graph. + * @g: Graph to manipulate. + * @n: Node to remove from the graph. + * + * Returns: The modified graph. + * + * NOTE: Undefined if the node is not in the graph. + */ +graph *graph_delete_node(graph *g, node *n); + +/** + * graph_delete_edge() - Remove an edge from the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * Returns: The modified graph. + * + * NOTE: Undefined if the edge is not in the graph. + */ +graph *graph_delete_edge(graph *g, node *n1, node *n2); + +/** + * graph_choose_node() - Return an arbitrary node from the graph. + * @g: Graph to inspect. + * + * Returns: A pointer to an arbitrayry node. + * + * NOTE: The return value is undefined for an empty graph. + */ +node *graph_choose_node(const graph *g); + +/** + * graph_neighbours() - Return a list of neighbour nodes. + * @g: Graph to inspect. + * @n: Node to get neighbours for. + * + * Returns: A pointer to a list of nodes. Note: The list must be + * dlist_kill()-ed after use. + */ +dlist *graph_neighbours(const graph *g,const node *n); + +/** + * graph_kill() - Destroy a given graph. + * @g: Graph to destroy. + * + * Return all dynamic memory used by the graph. + * + * Returns: Nothing. + */ +void graph_kill(graph *g); + +/** + * graph_print() - Iterate over the graph elements and print their values. + * @g: Graph to inspect. + * + * Iterates over the graph and prints its contents. + * + * Returns: Nothing. + */ +void graph_print(const graph *g); + +#endif diff --git a/OU4/graph2 b/OU4/graph2 new file mode 100755 index 0000000..1537624 Binary files /dev/null and b/OU4/graph2 differ diff --git a/OU4/graph2 before Yankob Edit.c b/OU4/graph2 before Yankob Edit.c new file mode 100644 index 0000000..802bbee --- /dev/null +++ b/OU4/graph2 before Yankob Edit.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include +#include + +struct graph +{ + dlist *nodes; // The graph nodes are stored in a directed list + int max_nodes; + // int connected; +}; + + +struct node +{ + void *value; + bool seen; + dlist *edge; +}; + + + + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * node_create() - Allocate and populate a graph entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created graph entry. + */ +node *node_create(const void *value, int max_node) +{ + // Allocate space for a graph entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + node *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->value = value; + e->seen = false; + e->edge = dlist_empty(NULL); // lista med tillhörande edges + return e; +} + +/** + * node_kill() - Return the memory allocated to a graph entry. + * @e: The graph entry to deallocate. + * + * Returns: Nothing. + */ +void node_kill(void *v) +{ + node *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * graph_empty() - Create an empty graph. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new graph. + */ +graph *graph_empty(int max_nodes) +{ + // Allocate the graph header. + graph *g = calloc(1, sizeof(graph)); + // Create the list to hold the node-ies. + g->nodes = dlist_empty(NULL); + g->max_nodes = max_nodes; + // Store the key compare function and key/value kill functions. + + return g; +} + +/** + * graph_is_empty() - Check if a graph is empty. + * @graph: graph to check. + * + * Returns: True if graph contains no entries, false otherwise. + */ +bool graph_is_empty(const graph *g) +{ + return dlist_is_empty(g->nodes); +} + + +graph *graph_insert_node(graph *g, const char *s) +{ + node *e = node_create(s, g->max_nodes); + //e->seen = false; + + dlist_insert(g->nodes, e, dlist_first(g->nodes)); + return g; +} + +bool nodes_are_equal(const node *n1, const node *n2) +{ + if (n1 == NULL || n2 == NULL) + { + return n1 == n2; + } + return strcmp(n1->value, n2->value) == 0; +} + +bool graph_has_edges(const graph *g) +{ + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + if (!dlist_is_empty(e->edge)) + { + return true; + } + pos = dlist_next(g->nodes, pos); + } + return false; +} + +node *graph_find_node(const graph *g, const char *s) +{ + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + if (strcmp(e->value, s) == 0 && !e->edge) + { + return e; + } + pos = dlist_next(g->nodes, pos); + } + return NULL; +} + +bool graph_node_is_seen(const graph *g, const node *n) +{ + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + if (nodes_are_equal(e, n) && e->edge == false) + { + return e->seen; + } + pos = dlist_next(g->nodes, pos); + } + return false; +} + +graph *graph_node_set_seen(graph *g, node *n, bool seen) +{ + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + if (nodes_are_equal(e, n) && e->edge == false) + { + e->seen = seen; + break; + } + pos = dlist_next(g->nodes, pos); + } + return g; +} + +graph *graph_reset_seen(graph *g) +{ + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + if (e->seen == true) + { + e->seen = false; + } + pos = dlist_next(g->nodes, pos); + } + return g; +} + +graph *graph_insert_edge(graph *g, node *n1, node *n2) +{ + dlist_insert(n1->edge, n2, dlist_first(n1->edge)); + return g; +} + + + +dlist *graph_neighbours(const graph *g,const node *n) +{ + return n->edge; +} + + + +void graph_print(const graph *g) +{ + dlist_pos pos = dlist_first(g->nodes); + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + printf("%s to nodes: ", (char *)e->value); + dlist_pos pos_edge = dlist_first(e->edge); + while (!dlist_is_end(e->edge, pos_edge)) + { + node *edges = dlist_inspect(e->edge,pos_edge); + printf("%s,", (char *)edges->value); + pos_edge = dlist_next(e->edge, pos_edge); + } + printf("\n"); + + pos = dlist_next(g->nodes, pos); + } +} diff --git a/OU4/graph2-marctest b/OU4/graph2-marctest new file mode 100644 index 0000000..ea276b5 Binary files /dev/null and b/OU4/graph2-marctest differ diff --git a/OU4/graph2.c b/OU4/graph2.c new file mode 100644 index 0000000..0f34727 --- /dev/null +++ b/OU4/graph2.c @@ -0,0 +1,407 @@ +#include +#include +#include +#include +#include +#include + + +/* + * graph2.c: Implementation of a dlist based graph for "Obligatorisk uppgift 4" in the "Datastructures and + * algorithms" course at the Department of Computing Science, Umea University. + * + * Authors: Marc Meunier tfy22mmr + * Ellen Horneij tfy22ehj + * Jakob Nyström tfy22jnm + * + * Version information: + * v1.0 2025-05-25: First public version. + * + */ + +// ===========INTERNAL DATA TYPES ============ + +struct graph +{ + dlist *nodes; // The graph nodes are stored in a directed list + int max_nodes; + // int connected; +}; + +struct node +{ + const char *value; + bool seen; + dlist *edge; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * node_entry_kill() - Return the memory allocated to a node entry. + * @e: The entry to deallocate. + * + * Returns: Nothing. + */ +void node_entry_kill(void *v) +{ + node *n = v; + free((void*)n->value); // Cast away const and free the string + dlist_kill(n->edge); // kills list + free(n); // frees memory + +} + +/** + * graph_entry_create() - Allocate and populate a graph entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created graph entry. + */ +node *node_entry_create(const char *value, int max_node) +{ + // Allocate space for a graph entry. Use calloc as a defensive + node *n = calloc(1, sizeof(*n)); + + // Populate the entry. + char *tmp = malloc(strlen(value) + 1); + if (tmp != NULL) + { + strcpy(tmp, value); + n->value = tmp; + } + else + { + // Handle memory allocation failure + free(n); + return NULL; + } + + // Set status to false, and create the list of edges + n->seen = false; + n->edge = dlist_empty(NULL); + return n; +} + +// =================== GRAPH STRUCTURE INTERFACE ====================== + +/** + * nodes_are_equal() - Check whether two nodes are equal. + * @n1: Pointer to node 1. + * @n2: Pointer to node 2. + * + * Returns: true if the nodes are considered equal, otherwise false. + * + */ +bool nodes_are_equal(const node *n1, const node *n2) +{ + if (n1 == NULL || n2 == NULL) + { + return n1 == n2; + } + // compares string values of nodes + return strcmp(n1->value, n2->value) == 0; +} + +/** + * graph_empty() - Create an empty graph. + * @max_nodes: The maximum number of nodes the graph can hold. + * + * Returns: A pointer to the new graph. + */ +graph *graph_empty(int max_nodes) +{ + // Allocate the graph + graph *g = calloc(1, sizeof(graph)); + + // Create the list to hold the graph_entry-ies. + g->nodes = dlist_empty(node_entry_kill); + g->max_nodes = max_nodes; + return g; +} + +/* + * graph_is_empty() - Check if a graph is empty, i.e. has no nodes. + * @g: Graph to check. + * + * Returns: True if graph is empty, otherwise false. + */ +bool graph_is_empty(const graph *g) +{ + return dlist_is_empty(g->nodes); +} + +/** + * graph_insert_node() - Inserts a node with the given name into the graph. + * @g: Graph to manipulate. + * @s: Node name. + * + * Creates a new node with a copy of the given name and puts it into + * the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_node(graph *g, const char *s) +{ + // create node + node *n = node_entry_create(s, g->max_nodes); + n->seen = false; + + // get start position of list + dlist_pos pos = dlist_first(g->nodes); + + // iterate over list + while (!dlist_is_end(g->nodes, pos)) + { + // check node already exists + node *m = dlist_inspect(g->nodes, pos); + if (nodes_are_equal(n, m)) + { + node_entry_kill(n); + return g; + } + // go to next position + pos = dlist_next(g->nodes, pos); + } + // inserts node into list + dlist_insert(g->nodes, n, dlist_first(g->nodes)); + return g; +} + +/** + * graph_has_edges() - Check if a graph has any edges. + * @g: Graph to check. + * + * Returns: True if graph has any edges, otherwise false. + */ +bool graph_has_edges(const graph *g) +{ + // get start position + dlist_pos pos = dlist_first(g->nodes); + + // check for edges for every node + while (!dlist_is_end(g->nodes, pos)) + { + node *n = dlist_inspect(g->nodes, pos); + if (!dlist_is_empty(n->edge)) + { + return true; + } + // go to next position + pos = dlist_next(g->nodes, pos); + } + return false; +} + +/** + * graph_find_node() - Find a node stored in the graph. + * @g: Graph to manipulate. + * @s: Node identifier, e.g. a char *. + * + * Returns: A pointer to the found node, or NULL. + */ +node *graph_find_node(const graph *g, const char *s) +{ + // get start position + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *n = dlist_inspect(g->nodes, pos); + // compare value with the one we are looking for + if (strcmp(n->value, s) == 0) + { + return n; + } + // go to next position + pos = dlist_next(g->nodes, pos); + } + return NULL; +} +/** + * graph_node_is_seen() - Return the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to return seen status for. + * + * Returns: The seen status for the node. + */ +bool graph_node_is_seen(const graph *g, const node *n) +{ + // get start position + dlist_pos pos = dlist_first(g->nodes); + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + // if node is one we are looking for, check seen status + if (nodes_are_equal(e, n)) + { + return e->seen; + } + // increment position + pos = dlist_next(g->nodes, pos); + } + return false; +} + +/** + * graph_node_set_seen() - Set the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to set seen status for. + * @s: Status to set. + * + * Returns: The modified graph. + */ +graph *graph_node_set_seen(graph *g, node *n, bool seen) +{ + // get beginning position + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + node *e = dlist_inspect(g->nodes, pos); + // check if nodes are same as one we look for + if (nodes_are_equal(e, n)) + { + // set node to seen + e->seen = seen; + break; + } + // increment pos in list + pos = dlist_next(g->nodes, pos); + } + return g; +} + +/** + * graph_reset_seen() - Reset the seen status on all nodes in the graph. + * @g: Graph to modify. + * + * Returns: The modified graph. + */ +graph *graph_reset_seen(graph *g) +{ + // get starting position + dlist_pos pos = dlist_first(g->nodes); + + while (!dlist_is_end(g->nodes, pos)) + { + // if node is seen, reset + node *n = dlist_inspect(g->nodes, pos); + if (n->seen == true) + { + n->seen = false; + } + // increment over list + pos = dlist_next(g->nodes, pos); + } + return g; +} + +/** + * graph_insert_edge() - Insert an edge into the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * NOTE: Undefined unless both nodes are already in the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_edge(graph *g, node *n1, node *n2) +{ + // get starting position + dlist_pos pos = dlist_first(n1->edge); + while (!dlist_is_end(n1->edge, pos)) + { + node *n = dlist_inspect(n1->edge, pos); + // check if node already is in edge list + if (nodes_are_equal(n2, n)) + { + return g; + } + // increment to next position + pos = dlist_next(n1->edge, pos); + } + // inserts the edge in node list. + dlist_insert(n1->edge, n2, dlist_first(n1->edge)); + return g; +} + +/** + * graph_neighbours() - Return a list of neighbour nodes. + * @g: Graph to inspect. + * @n: Node to get neighbours for. + * + * Returns: A pointer to a list of nodes. Note: The list must be + * dlist_kill()-ed after use. + */ +dlist *graph_neighbours(const graph *g, const node *n) +{ + // Create a new list + dlist *copy = dlist_empty(NULL); + + if (n == NULL || n->edge == NULL) { + return copy; + } + + // Copy nodes to new list, so list can be dlost_kill-ed outside. + dlist_pos pos = dlist_first(n->edge); + while (!dlist_is_end(n->edge, pos)) { + void *node = dlist_inspect(n->edge, pos); + dlist_insert(copy, node, dlist_first(copy)); + pos = dlist_next(n->edge, pos); + } + + return copy; +} + +/** + * graph_kill() - Destroy a given graph. + * @g: Graph to destroy. + * + * Return all dynamic memory used by the graph. + * + * Returns: Nothing. + */ +void graph_kill(graph *g) +{ + dlist_kill(g->nodes); + free(g); +} + +/** + * graph_print() - Iterate over the graph elements and print their values. + * @g: Graph to inspect. + * + * Iterates over the graph and prints its contents. + * + * Returns: Nothing. + */ +void graph_print(const graph *g) +{ + // get first position + dlist_pos pos = dlist_first(g->nodes); + // iterate and prints node + while (!dlist_is_end(g->nodes, pos)) + { + node *n = dlist_inspect(g->nodes, pos); + printf("%s to nodes: ", (char *)n->value); + + // get edges pos, and print edges of node. + dlist_pos pos_edge = dlist_first(n->edge); + while (!dlist_is_end(n->edge, pos_edge)) + { + node *e = dlist_inspect(n->edge, pos_edge); + printf("%s,", (char *)e->value); + + // goes to next edge in list + pos_edge = dlist_next(n->edge, pos_edge); + } + printf("\n"); + + // goes to next node in list. + pos = dlist_next(g->nodes, pos); + } +} diff --git a/OU4/implementation.txt b/OU4/implementation.txt new file mode 100644 index 0000000..fa771ce --- /dev/null +++ b/OU4/implementation.txt @@ -0,0 +1,33 @@ +Vad som ska göras + + 1. Huvudprogram som löser inmatning från användaren + + * Ska läsa in grafen från kartfilen interagera med användaren och köra sökalgoritmen se punkt 3 + * Efter start ska "origin" och "destination" efterfrågas + * Om de två noderna är sammanlänkade ska programmet meddela det + * Om "origin" och "destination" är samma anses de vara sammanlänkade + * Om "quit" skrivs ska programmet avslutas + * Om felaktig indata anges ska ett lämpligt felmeddelande skrivas ut + * Utskrifterna ska följa specifikiationen exakt och göras med printf() + + 'Enter origin and destination (quit to exit): ' + + * Om kart-filen inte har ett korrekt format ska programmet avslutas med exit(EXIT_FAILURE) + + 2. Uppbyggnad av implementationen av graph1/2 + + * Gränsytan finns beskriven i graph.h + * Läs noga info om graph_neighbours() + * Valfritt hur noderna representeras + * Två olika implementationer av graph ska göras + * graph.c + + * Ska använda sig av array_2d internt + + * graph2.c + + * Valfri implementation men rekommenderat att använda fält av lista + + 3. Sorteringsalgoritm + + * Två strängar anses lika om hjälpfunktionen bool nodes_are_equal returnerar true diff --git a/OU4/include/array_1d.h b/OU4/include/array_1d.h new file mode 100644 index 0000000..95faae1 --- /dev/null +++ b/OU4/include/array_1d.h @@ -0,0 +1,150 @@ +#ifndef __ARRAY_1D_H +#define __ARRAY_1D_H + +#include +#include "util.h" + +/* + * Declaration of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The array stores void pointers, so it can be used to + * store all types of values. After use, the function array_kill must + * be called to de-allocate the dynamic memory used by the array + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the array, + * unless a kill_function is registered in array_create. + * + * An element value of NULL is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct array_1d array_1d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_1d *array_1d_create(int lo, int hi, kill_function kill_func); + +/** + * array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int array_1d_low(const array_1d *a); + +/** + * array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int array_1d_high(const array_1d *a); + +/** + * array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_1d_inspect_value(const array_1d *a, int i); + +/** + * array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_1d_has_value(const array_1d *a, int i); + +/** + * array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_1d_set_value(array_1d *a, void *v, int i); + +/** + * array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_1d_kill(array_1d *a); + +/** + * array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_1d_print(const array_1d * l, inspect_callback print_func); + +/** + * array_1d_print_internal() - Print the internal structure of the array in dot format. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/array_2d.h b/OU4/include/array_2d.h new file mode 100644 index 0000000..3020ece --- /dev/null +++ b/OU4/include/array_2d.h @@ -0,0 +1,158 @@ +#ifndef __ARRAY_2D_H +#define __ARRAY_2D_H + +#include +#include "util.h" + +/* + * Declaration of a generic 2D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The array stores void pointers, so it can be used to + * store all types of values. After use, the function array_kill must + * be called to de-allocate the dynamic memory used by the array + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the array, + * unless a kill_function is registered in array_create. + * + * An element value of NULL is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Moved freehandler to last in create parameter list. + * v1.2 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct array_2d array_2d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * array_2d_create() - Create an array without values. + * @lo1: low index limit for first dimension. + * @hi1: high index limit for first dimension. + * @lo2: low index limit for second dimension. + * @hi2: high index limit for second dimension. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2, + kill_function kill_func); + +/** + * array_2d_low() - Return the low index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The low index limit for dimension number d. + */ +int array_2d_low(const array_2d *a, int d); + +/** + * array_2d_high() - Return the high index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The high index limit for dimension number d. + */ +int array_2d_high(const array_2d *a, int d); + +/** + * array_2d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_2d_inspect_value(const array_2d *a, int i, int j); + +/** + * array_2d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_2d_has_value(const array_2d *a, int i, int j); + +/** + * array_2d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: first index of position to inspect. + * @j: second index of position to inspect. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_2d_set_value(array_2d *a, void *v, int i, int j); + +/** + * array_2d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_2d_kill(array_2d *a); + +/** + * array_2d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_2d_print(const array_2d * l, inspect_callback print_func); + +/** + * array_2d_print_internal() - Print the arrays internal structure. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/dlist.h b/OU4/include/dlist.h new file mode 100644 index 0000000..8900bc8 --- /dev/null +++ b/OU4/include/dlist.h @@ -0,0 +1,199 @@ +#ifndef __DLIST_H +#define __DLIST_H + +#include +#include "util.h" + +/* + * Declaration of a generic, directed list for the "Datastructures + * and algorithms" courses at the Department of Computing Science, + * Umea University. The list stores void pointers, so it can be used + * to store all types of values. After use, the function list_kill + * must be called to de-allocate the dynamic memory used by the list + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the list, + * unless a kill_function is registered in list_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added dlist_pos_equal and dlist_pos_is_valid functions. + * v1.2 2023-01-20: Renamed dlist_pos_equal to dlist_pos_are_equal. + * v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct dlist dlist; + +// List position type. +typedef struct cell *dlist_pos; + + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * dlist_empty() - Create an empty dlist. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +dlist *dlist_empty(kill_function kill_func); + +/** + * dlist_is_empty() - Check if a dlist is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool dlist_is_empty(const dlist *l); + +/** + * dlist_first() - Return the first position of a dlist, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +dlist_pos dlist_first(const dlist *l); + +/** + * dlist_next() - Return the next position in a dlist. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +dlist_pos dlist_next(const dlist *l, const dlist_pos p); + +/** + * dlist_is_end() - Check if a given position is at the end of a dlist. + * @l: List to inspect. + * @p: Any valid position in the list. + * + * Returns: True if p is at the end of the list. + */ +bool dlist_is_end(const dlist *l, const dlist_pos p); + +/** + * dlist_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *dlist_inspect(const dlist *l, const dlist_pos p); + +/** + * dlist_insert() - Insert a new element with a given value into a dlist. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p); + +/** + * dlist_remove() - Remove an element from a dlist. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +dlist_pos dlist_remove(dlist *l, const dlist_pos p); + +/** + * dlist_kill() - Destroy a given dlist. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void dlist_kill(dlist *l); + +/** + * dlist_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void dlist_print(const dlist *l, inspect_callback print_func); + +/** + * dlist_pos_is_valid() - Return true for a valid position in a dlist + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool dlist_pos_is_valid(const dlist *l, const dlist_pos p); + +/** + * dlist_pos_is_equal() - Return true if two positions in a dlist are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2); + +/** + * dlist_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/graph.h b/OU4/include/graph.h new file mode 100644 index 0000000..103190b --- /dev/null +++ b/OU4/include/graph.h @@ -0,0 +1,200 @@ +#ifndef __GRAPH_H +#define __GRAPH_H + +#include +#include "util.h" +#include "dlist.h" + +/* + * Declaration of a generic graph for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The graph stores nodes and edges of a directed or + * undirected graph. After use, the function graph_kill() must + * be called to de-allocate the dynamic memory used by the graph + * itself. The de-allocation of any dynamic memory allocated for the + * node names is the responsibility of the user of the graph. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se) + * + * Version information: + * v1.0 2019-02-21: First public version. + + * v1.01 2019-02-26: Modified include directives for header files + * from the codebase to use "" to handle case when + * student stores all header files in the current + * directory. See + * https://stackoverflow.com/questions/21593/what-is-the-difference-between-include-filename-and-include-filename. + * v1.02 2019-03-01: Doc update in graph_node_is_seen(). + * v1.1 2019-03-06: Changed several const node * to node *. + * Fixed doc bug to state that any dynamic memory allocated + * to the node NAMES is the resposibility of the user. + */ + +// ====================== PUBLIC DATA TYPES ========================== + +// Anonymous declarations of node and graph. +typedef struct node node; +typedef struct graph graph; + +// =================== NODE COMPARISON FUNCTION ====================== + +/** + * nodes_are_equal() - Check whether two nodes are equal. + * @n1: Pointer to node 1. + * @n2: Pointer to node 2. + * + * Returns: true if the nodes are considered equal, otherwise false. + * + */ +bool nodes_are_equal(const node *n1,const node *n2); + +// =================== GRAPH STRUCTURE INTERFACE ====================== + +/** + * graph_empty() - Create an empty graph. + * @max_nodes: The maximum number of nodes the graph can hold. + * + * Returns: A pointer to the new graph. + */ +graph *graph_empty(int max_nodes); + +/** + * graph_is_empty() - Check if a graph is empty, i.e. has no nodes. + * @g: Graph to check. + * + * Returns: True if graph is empty, otherwise false. + */ +bool graph_is_empty(const graph *g); + +/** + * graph_has_edges() - Check if a graph has any edges. + * @g: Graph to check. + * + * Returns: True if graph has any edges, otherwise false. + */ +bool graph_has_edges(const graph *g); + +/** + * graph_insert_node() - Inserts a node with the given name into the graph. + * @g: Graph to manipulate. + * @s: Node name. + * + * Creates a new node with a copy of the given name and puts it into + * the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_node(graph *g, const char *s); + +/** + * graph_find_node() - Find a node stored in the graph. + * @g: Graph to manipulate. + * @s: Node identifier, e.g. a char *. + * + * Returns: A pointer to the found node, or NULL. + */ +node *graph_find_node(const graph *g, const char *s); + +/** + * graph_node_is_seen() - Return the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to return seen status for. + * + * Returns: The seen status for the node. + */ +bool graph_node_is_seen(const graph *g, const node *n); + +/** + * graph_node_set_seen() - Set the seen status for a node. + * @g: Graph storing the node. + * @n: Node in the graph to set seen status for. + * @s: Status to set. + * + * Returns: The modified graph. + */ +graph *graph_node_set_seen(graph *g, node *n, bool seen); + +/** + * graph_reset_seen() - Reset the seen status on all nodes in the graph. + * @g: Graph to modify. + * + * Returns: The modified graph. + */ +graph *graph_reset_seen(graph *g); + +/** + * graph_insert_edge() - Insert an edge into the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * NOTE: Undefined unless both nodes are already in the graph. + * + * Returns: The modified graph. + */ +graph *graph_insert_edge(graph *g, node *n1, node *n2); + +/** + * graph_delete_node() - Remove a node from the graph. + * @g: Graph to manipulate. + * @n: Node to remove from the graph. + * + * Returns: The modified graph. + * + * NOTE: Undefined if the node is not in the graph. + */ +graph *graph_delete_node(graph *g, node *n); + +/** + * graph_delete_edge() - Remove an edge from the graph. + * @g: Graph to manipulate. + * @n1: Source node (pointer) for the edge. + * @n2: Destination node (pointer) for the edge. + * + * Returns: The modified graph. + * + * NOTE: Undefined if the edge is not in the graph. + */ +graph *graph_delete_edge(graph *g, node *n1, node *n2); + +/** + * graph_choose_node() - Return an arbitrary node from the graph. + * @g: Graph to inspect. + * + * Returns: A pointer to an arbitrayry node. + * + * NOTE: The return value is undefined for an empty graph. + */ +node *graph_choose_node(const graph *g); + +/** + * graph_neighbours() - Return a list of neighbour nodes. + * @g: Graph to inspect. + * @n: Node to get neighbours for. + * + * Returns: A pointer to a list of nodes. Note: The list must be + * dlist_kill()-ed after use. + */ +dlist *graph_neighbours(const graph *g,const node *n); + +/** + * graph_kill() - Destroy a given graph. + * @g: Graph to destroy. + * + * Return all dynamic memory used by the graph. + * + * Returns: Nothing. + */ +void graph_kill(graph *g); + +/** + * graph_print() - Iterate over the graph elements and print their values. + * @g: Graph to inspect. + * + * Iterates over the graph and prints its contents. + * + * Returns: Nothing. + */ +void graph_print(const graph *g); + +#endif diff --git a/OU4/include/int_array_1d.h b/OU4/include/int_array_1d.h new file mode 100644 index 0000000..9b53387 --- /dev/null +++ b/OU4/include/int_array_1d.h @@ -0,0 +1,136 @@ +#ifndef __INT_ARRAY_1D_H +#define __INT_ARRAY_1D_H + +#include +#include "util.h" + +/* + * Declaration of an integer 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. After use, the function array_kill must be called to + * de-allocate the dynamic memory used by the array itself. + * + * An element value of 0 is considered to be "no" value. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.2 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct int_array_1d int_array_1d; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * int_array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +int_array_1d *int_array_1d_create(int lo, int hi); + +/** + * int_array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int int_array_1d_low(const int_array_1d *a); + +/** + * int_array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int int_array_1d_high(const int_array_1d *a); + +/** + * int_array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position, or 0 if no + * value is stored at that position. + */ +int int_array_1d_inspect_value(const int_array_1d *a, int i); + +/** + * int_array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool int_array_1d_has_value(const int_array_1d *a, int i); + +/** + * int_array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or 0 to clear value. + * @i: index of position to modify. + * + * Returns: Nothing. + */ +void int_array_1d_set_value(int_array_1d *a, int v, int i); + +/** + * int_array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If free_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void int_array_1d_kill(int_array_1d *a); + +/** + * int_array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * + * Iterates over each position in the array. Prints each non-zero element. + * + * Returns: Nothing. + */ +void int_array_1d_print(const int_array_1d *a); + +/** + * int_array_1d_print_internal() - Print the arrays internal structure in dot format. + * @a: Array to inspect. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level); + +#endif diff --git a/OU4/include/int_list.h b/OU4/include/int_list.h new file mode 100644 index 0000000..30e9549 --- /dev/null +++ b/OU4/include/int_list.h @@ -0,0 +1,192 @@ +#ifndef __INT_LIST_H +#define __INT_LIST_H + +#include +#include "util.h" + +/* + * Declaration of a undirected list for storing integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * dynamic, linked structure. After use, the function list_kill must + * be called to de-allocate the dynamic memory used by the list + * itself. The implementation is a written a code copy specialization + * of the generic list to provide a simpler starting data structure + * than the generic list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef struct cell *list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @data: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, int data, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * + * Iterates over the list and prints each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list *l); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level); + +#endif diff --git a/OU4/include/int_list_array.h b/OU4/include/int_list_array.h new file mode 100644 index 0000000..9db2f74 --- /dev/null +++ b/OU4/include/int_list_array.h @@ -0,0 +1,193 @@ +#ifndef __INT_LIST_ARRAY_H +#define __INT_LIST_ARRAY_H + +#include +#include "util.h" + +/* + * Declaration of a undirected list for storing integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * static array. After use, the function list_kill must be called to + * de-allocate the dynamic memory used by the list itself. The + * implementation is a written a code copy specialization of the + * generic list to provide a simpler starting data structure than the + * generic list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-03-26: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * Bugfix in list_remove. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef int list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @data: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, int data, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * + * Iterates over the list and prints each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list *l); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level); + +#endif diff --git a/OU4/include/int_queue.h b/OU4/include/int_queue.h new file mode 100644 index 0000000..45a8c2e --- /dev/null +++ b/OU4/include/int_queue.h @@ -0,0 +1,126 @@ +#ifndef __INT_QUEUE_H +#define __INT_QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of an integer queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores integers directly and does not use + * dynamic memory. Thus, the clean-up function queue_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic queue implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +// ==========PUBLIC CONSTANTS AND DATA TYPES============ + +// Maximum size of the queue. +#define MAX_QUEUE_SIZE 100 + +// We must define the struct publically for the compiler to know its +// size for copying, etc. +typedef struct queue { + int first_free_pos; + int elements[MAX_QUEUE_SIZE]; +} queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * + * Returns: A new, empty, queue. + */ +queue queue_empty(void); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue q); + +/** + * queue_enqueue() - Put a value at the end of a queue. + * @q: Queue to manipulate. + * @v: Value (integer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue queue_enqueue(queue q, int v); + +/** + * queue_dequeue() - Remove the element at the beginning of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue queue_dequeue(queue q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the front of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +int queue_front(const queue q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Does nothing since the queue does not use any dynamic + * memory. Included for symmetry with generic queue.h. + * + * Returns: Nothing. + */ +void queue_kill(queue q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * + * Iterates over the queue and prints each integer. + * + * Returns: Nothing. + */ +void queue_print(const queue q); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the queue and outputs dot code that shows the + * internal structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems); + +#endif diff --git a/OU4/include/int_stack.h b/OU4/include/int_stack.h new file mode 100644 index 0000000..a64cb28 --- /dev/null +++ b/OU4/include/int_stack.h @@ -0,0 +1,134 @@ +#ifndef __INT_STACK_H +#define __INT_STACK_H + +#include +#include "util.h" + +/* + * Declaration of an integer stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores integers directly and does not use + * dynamic memory. Thus, the clean-up function stack_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic stack implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-04-15: Reduced default stack size to 15 to make output + * from print_internal readable. + * v1.2 2024-05-10: Updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Stack type. +#define MAX_STACK_SIZE 100 + +// We must define the struct publically for the compiler to know its +// size for copying, etc. +typedef struct stack { + int first_free_pos; + int elements[MAX_STACK_SIZE]; +} stack; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * stack_empty() - Create an empty stack. + * + * Returns: A new stack. + */ +stack stack_empty(void); + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack s); + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (integer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_push(stack s, int v); + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_pop(stack s); + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +int stack_top(const stack s); + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Does nothing since the stack does not use any dynamic + * memory. Included for symmetry with generic stack.h. + * + * Returns: Nothing. + */ +void stack_kill(stack s); + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * + * Iterates over the stack and prints each integer. + * + * Returns: Nothing. + */ +void stack_print(const stack s); + +/** + * stack_print_internal() - Print the internal structure of the stack in dot format. + * @s: Stack to inspect. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the stack and outputs dot code that shows the + * internal structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems); + +#endif diff --git a/OU4/include/list.h b/OU4/include/list.h new file mode 100644 index 0000000..a65d7bd --- /dev/null +++ b/OU4/include/list.h @@ -0,0 +1,207 @@ +#ifndef __LIST_H +#define __LIST_H + +#include +#include "util.h" + +/* + * Declaration of a generic, undirected list for the "Datastructures + * and algorithms" courses at the Department of Computing Science, + * Umea University. The list stores void pointers, so it can be used + * to store all types of values. After use, the function list_kill + * must be called to de-allocate the dynamic memory used by the list + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the list, + * unless a kill_function is registered in list_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v2.0 2024-03-14: Added list_print_internal to output dot code for visualization. + * v2.1 2024-05-10: Updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// List type. +typedef struct list list; + +// List position type. +typedef struct cell *list_pos; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * list_empty() - Create an empty list. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +list *list_empty(kill_function kill_func); + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l); + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l); + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l); + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p); + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p); + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *list_inspect(const list *l, const list_pos p); + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, void *v, const list_pos p); + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p); + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void list_kill(list *l); + +/** + * list_print() - Iterate over the list elements and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void list_print(const list *l, inspect_callback print_func); + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p); + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + * p1 and p2 are assumed to be valid positions in l, no check is performed. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2); + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/queue.h b/OU4/include/queue.h new file mode 100644 index 0000000..1070e98 --- /dev/null +++ b/OU4/include/queue.h @@ -0,0 +1,131 @@ +#ifndef __QUEUE_H +#define __QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores void pointers, so it can be used to + * store all types of values. After use, the function queue_kill must + * be called to de-allocate the dynamic memory used by the queue + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the queue, + * unless a kill_function is registered in queue_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Queue type. +typedef struct queue queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q); + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v); + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/stack.h b/OU4/include/stack.h new file mode 100644 index 0000000..2dfef95 --- /dev/null +++ b/OU4/include/stack.h @@ -0,0 +1,133 @@ +#ifndef __STACK_H +#define __STACK_H + +#include +#include "util.h" + +/* + * Declaration of a generic stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores void pointers, so it can be used to + * store all types of values. After use, the function stack_kill must + * be called to de-allocate the dynamic memory used by the stack + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the stack, + * unless a kill_function is registered in stack_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Stack type. +typedef struct stack stack; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * stack_empty() - Create an empty stack. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new stack. + */ +stack *stack_empty(kill_function kill_func); + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack *s); + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (pointer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_push(stack *s, void *v); + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_pop(stack *s); + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +void *stack_top(const stack *s); + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Return all dynamic memory used by the stack and its elements. If a + * kill_func was registered at stack creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void stack_kill(stack *s); + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * @print_func: Function called for each element. + * + * Iterates over the stack and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void stack_print(const stack *s, inspect_callback print_func); + +/** + * stack_print_internal() - Print the stacks internal structure in dot format. + * @l: Stack to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the stack and outputs dot code that shows the internal + * structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/table.h b/OU4/include/table.h new file mode 100644 index 0000000..1a7d309 --- /dev/null +++ b/OU4/include/table.h @@ -0,0 +1,157 @@ +#ifndef TABLE_H +#define TABLE_H + +#include +#include "util.h" + +/* + * Declaration of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The table stores void pointers, so it can be used to + * store all types of values. After use, the function table_kill must + * be called to de-allocate the dynamic memory used by the table + * itself. The de-allocation of any dynamic memory allocated for the + * key and/or value values is the responsibility of the user of the + * table, unless a corresponding kill_function is registered in + * table_empty. + * + * The internal handling of duplicates are transparent to the user of + * the table. Depending on the table design, a duplicate may or may + * not be stored internally. The user of the table should not make any + * assumption about which solution is implemented. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2024-04-15: Changed the comments to leave room for different + * internal handling of duplicates. + * v1.2 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ +// Table type. +typedef struct table table; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. See + * util.h for the definition of compare_function. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func); + +/** + * table_is_empty() - Check if a table is empty. + * @t: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t); + +/** + * table_insert() - Add a key/value pair to a table. + * @t: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. If the key already + * exists, the key/value overwrites the existing pair. The technical + * details are internal to the datatype. At any rate, table_lookup() + * will return the latest added value for a duplicate + * key and table_remove() will remove all duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value); + +/** + * table_lookup() - Look up a given key in a table. + * @t: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key); + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t); + +/** + * table_remove() - Remove a key/value pair in the table. + * @t: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key); + +/** + * table_kill() - Destroy a table. + * @t: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to free any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t); + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including any duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func); + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/include/util.h b/OU4/include/util.h new file mode 100644 index 0000000..db3681e --- /dev/null +++ b/OU4/include/util.h @@ -0,0 +1,83 @@ +#ifndef __UTIL_H +#define __UTIL_H + +/* + * Utility function types for deallocating, printing and comparing + * values stored by various data types. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-02-06: Updated explanation for the compare_function. + * v1.2 2023-01-14: Added version define constants and strings. + * v1.3 2024-03-13: Added PTR2ADDR macro. + */ + +// Macros to create a version string out of version constants +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +// Version constants +#define CODE_BASE_MAJOR_VERSION 2 +#define CODE_BASE_MINOR_VERSION 2 +#define CODE_BASE_REVISION 2 +#define CODE_BASE_PATCH 2 + +#define CODE_BASE_RELEASE_DATE "2025-01-24" + +// Create a short version string +#define CODE_BASE_VERSION "v" \ + STR(CODE_BASE_MAJOR_VERSION) \ + "." \ + STR(CODE_BASE_MINOR_VERSION) \ + "." \ + STR(CODE_BASE_REVISION) \ + "." \ + STR(CODE_BASE_PATCH) + +// Create a version string +#define CODE_BASE_LONG_VERSION "Version: " \ + STR(CODE_BASE_MAJOR_VERSION) \ + "." \ + STR(CODE_BASE_MINOR_VERSION) \ + "." \ + STR(CODE_BASE_REVISION) \ + "." \ + STR(CODE_BASE_PATCH) + +// Type definition for de-allocator function, e.g. free(). +typedef void (*kill_function)(void *); + +// For backwards compatibility with pre-v2.0 code. +typedef kill_function free_function; + +// Type definition for read-only callback for single-value containers, +// used by e.g. print functions. +typedef void (*inspect_callback)(const void *); + +// Ditto for dual-value containers. +typedef void (*inspect_callback_pair)(const void *, const void *); + +// Type definition for comparison function, used by e.g. table. +// +// Comparison functions should return values that indicate the order +// of the arguments. If the first argument is considered less/lower +// than the second, a negative value should be returned. If the first +// argument is considered more/higher than the second, a positive value +// should be returned. If the arguments are considered equal, a zero +// value should be returned. +typedef int compare_function(const void *,const void *); + +// Constant used by ptr2addr, used by various print_internal functions. +#define PTR2ADDR_MASK 0xffff + +// Macro that acts as a function to convert the address in the pointer +// p into an unsigned long value, keeping only the part indicated by +// the mask. +#define PTR2ADDR(p) (((unsigned long)p) & PTR2ADDR_MASK) + +#endif diff --git a/OU4/is_connected.c b/OU4/is_connected.c new file mode 100644 index 0000000..4e1ca72 --- /dev/null +++ b/OU4/is_connected.c @@ -0,0 +1,359 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXNODENAME 40 +#define BUFSIZE 400 + +/* + * is:connected.c: The main program for traversing a graph for "Obligatorisk uppgift 4" in the "Datastructures and + * algorithms" course at the Department of Computing Science, Umea University. + * + * Authors: Ellen Horneij tfy22ehj + * Jakob Nyström tfy22jnm + * Marc Meunier tfy22mmr + * + * Version information: + * v1.0 2025-05-25: First public version. + * v2.0 2025-06-02: Fixed traversing + * v3.0 2025-07-29: Fixed error messages + * + */ + +// ===========HELP FUNCTIONS ============ +/** + * @brief Find position of first non-whitespace character. + * + * @param s - string to search. + * @return int - Returns the position of the first non-whitespace character, or -1 if not found. + */ +int first_non_white_space(const char *s) +{ + int i = 0; // Start at first char. + + // Advance as long as we're loooking at white-space until we hit EOL. + while (s[i] && isspace(s[i])) + { + i++; + } + // We found a non-whitespace char. + if (s[i]) + { + // It was a proper characted. Return its position. + return i; + } + else + { + // It was the null terminator. Return fail. + return -1; + } +} + +/** + * @brief Determine if the string is blank. + * + * @param s - string to search. + * @return true if the line contains only whitespace chars. + * @return false if the line contains at least one non-whitespace char. + */ + +bool line_is_blank(const char *s) +{ + // Line is blank if it only contained white-space chars. + return first_non_white_space(s) < 0; +} + +/** + * @brief Determine if the string is a comment line. + * + * @param s - string to search. + * @return true if the line is a comment line. + * @return false if the line is not a comment line. + * + * A comment line has a hash sign '#' as the first non-whitespace char on the line. + */ +bool line_is_comment(const char *s) +{ + int i = first_non_white_space(s); + return (i >= 0 && s[i] == '#'); +} + +/** + * @brief Extract node names from a line from the map file. + * + * @param buf - Input buffer. + * @param n1 - Output buffer for the first node name. Must be at least MAXNODENAME+1 long. + * @param n2 - Ditto for the second node name. + * @return int - Returns the number of correctly parsed node names. If the return value is 2, both n1 + * and n2 contain node names. If the return value is less than 2, parsing of at least one node name + * failed, in which case the content of n1 and n2 are undefined. + */ +int parse_map_line(const char *buf, char *n1, char *n2) +{ + // Create a format string the will do the work. + char fmt[20]; + // This will generate the format string " %40s %40s" if MAXNODENAME is 40. + snprintf(fmt, sizeof(fmt), " %%%ds %%%ds", MAXNODENAME, MAXNODENAME); + + // sscanf does all the necessary parsing. + // Node names must be separated by whitespace. + // Whitespace before the first node name is allowed. + // Anything after the second node name is ignored. + int n = sscanf(buf, fmt, n1, n2); + + // The return value from sscanf contains the number of properly parsed format codes, i.e. the + // number of node names. + return n; +} + +/** + * FUNCTION TO TRAVERSE THE GRAPH + * @g: pointer to the graph + * @src: pointer to source node + * @dest: pointer to destination node + */ +bool find_path(graph *g, node *src, node *dest) +{ + + // Mark the starting node as seen + g = graph_node_set_seen(g, src, true); + dlist *neighbours = dlist_empty(NULL); + // Put it in an empty queue + queue *q = queue_enqueue(queue_empty(NULL), src); + while (!queue_is_empty(q)) + { + // pick the first node from queue + src = queue_front(q); + q = queue_dequeue(q); + + // if node equals destinatiom node, return true + if (nodes_are_equal(src, dest)) + { + // Deallocate the queue and list + queue_kill(q); + dlist_kill(neighbours); + return true; + } + // remove list before making new. + if (neighbours != NULL) + { + dlist_kill(neighbours); + } + // get the neighbours + neighbours = graph_neighbours(g, src); + dlist_pos p = dlist_first(neighbours); + + // Iterate over the lists of neigbours + while (!dlist_is_end(neighbours, p)) + { + + if (!graph_node_is_seen(g, dlist_inspect(neighbours, p))) + { + // Mark unseen node as seen and put in queue + g = graph_node_set_seen(g, dlist_inspect(neighbours, p), true); + q = queue_enqueue(q, dlist_inspect(neighbours, p)); + } + p = dlist_next(neighbours, p); + } + } + // Deallocate the list and the queue + dlist_kill(neighbours); + queue_kill(q); + return false; +} + +/** + * check_node_name - function used to find errors in node name + * + * Returns: Exit faliure if error in map file + */ +void check_node_name(char *n1, char *n2, char *data) +{ + int k = parse_map_line(data, n1, n2); + + //Print error if number of correct node names is not 2 + if (k != 2) + { + fprintf(stderr, "Error reading the file\n"); + exit(EXIT_FAILURE); + } + //Check for non alphanumeric characters + for (int j = 0; j < strlen(n1); j++) + { + if (isalnum(n1[j])==0) + { + fprintf(stderr, "Error non alphanumeric\n"); + exit(EXIT_FAILURE); + } + } + for (int i = 0; i < strlen(n2); i++) + { + if (isalnum(n2[i])==0) + { + fprintf(stderr, "Error non alphanumeric\n"); + exit(EXIT_FAILURE); + } + } + +} + +/** + * insert_nodes - insert the nodes to the graph + * Returns: A graph with the input nodes inserted + */ +graph *insert_nodes(graph *g, char *n1, char *n2) +{ + g = graph_insert_node(g, n1); + g = graph_insert_node(g, n2); + + // Get pointers to the nodes from the graph + node *m1 = graph_find_node(g, n1); + node *m2 = graph_find_node(g, n2); + + // Check that both nodes exist + if (m1 == NULL || m2 == NULL) + { + fprintf(stderr, "Error: node not found for edge: %s - %s\n", n1, n2); + exit(EXIT_FAILURE); + } + + // Insert the edges between the source and destination nodes and between the nodes themselves + g = graph_insert_edge(g, m1, m1); + g = graph_insert_edge(g, m2, m2); + g = graph_insert_edge(g, m1, m2); + + + return g; +} + +/** + * graph_from_file - reads the file and constructs a graph with it's contents + * Returns: A pointer to the constructed graph + */ +graph *graph_from_file(int argc, char *argv[]) +{ + char data[40]; + + // Open the file + if (argc != 2) + { + fprintf(stderr, "Usage: %s \n", argv[0]); + return NULL; + } + const char *filename = argv[1]; + + // Print error message if file did not read correctly an exit program + FILE *fptr = fopen(filename, "r"); + if (fptr == NULL) + { + perror("Error opening file\n"); + return NULL; + } + + // Read the first line of the map-file + fgets(data, 40, fptr); + + // Ignore comments and blank rows + while (line_is_blank(data) || line_is_comment(data)) + { + fgets(data, 40, fptr); + } + + //Check that first non blank or coment line is a digit + for (int i = 0; i < strlen(data) - 1; i++) + { + if (!isdigit(data[i])) + { + fprintf(stderr, "Error: first row is not a number\n"); + exit(EXIT_FAILURE); + } + } + // Read the number of edges in the map-file and create an empty graph + int nr_of_edges = atoi(data); + graph *g = graph_empty(2 * nr_of_edges); + + // Iterates over the edge imputs + for (int i = 0; i < nr_of_edges; i++) + { + // Read the data, parse the data and insert it into nodes in the graph + fgets(data, 40, fptr); + char n1[MAXNODENAME + 1]; + char n2[MAXNODENAME + 1]; + check_node_name(n1,n2,data); + g = insert_nodes(g,n1,n2); + } + // Close the file and return the graph + fclose(fptr); + return g; +} + +/** + * main - The main program that handles user inputs and runs all of the help functions + * + * Returns: 0 if program exits normally + * + */ +int main(int argc, char *argv[]) +{ + // Read the file and insert it into a graph + graph *g = graph_from_file(argc, argv); + + // If file opening fails + if (g == NULL) + { + return EXIT_FAILURE; + } + + char src[MAXNODENAME + 1]; + char dest[MAXNODENAME + 1]; + char fmt[20]; + char buf[100]; + + // Print the user interface + printf("Enter origin and destination (quit to exit): "); + + // This will generate the format string " %40s %40s" if MAXNODENAME is 40. + snprintf(fmt, sizeof(fmt), " %%%ds %%%ds", MAXNODENAME, MAXNODENAME); + + while (fgets(buf, sizeof(buf), stdin) != NULL) + { + // Reads the input from the user + sscanf(buf, fmt, src, dest); + + // Quit the program if the user enters "quit" + if (strcmp(src, "quit") == 0) + { + break; + } + + // Check that the nodes exist in the graph + node *nsrc = graph_find_node(g, src); + node *ndest = graph_find_node(g, dest); + if (nsrc == NULL || ndest == NULL) + { + printf("One or both nodes not found in the graph.\n"); + } + // If the nodes exist check if there is a path and print the result + else if (find_path(g, nsrc, ndest)) + { + printf("There is a path from %s to %s.\n\n", src, dest); + g = graph_reset_seen(g); + } + else + { + printf("There is no path from %s to %s.\n\n", src, dest); + g = graph_reset_seen(g); + } + printf("Enter origin and destination (quit to exit): "); + } + + // Print exit message and kill the graph + printf("Normal exit.\n"); + graph_kill(g); + return 0; +} diff --git a/OU4/is_connected2 b/OU4/is_connected2 new file mode 100644 index 0000000..5f51ad6 Binary files /dev/null and b/OU4/is_connected2 differ diff --git a/OU4/is_connected_backup.c b/OU4/is_connected_backup.c new file mode 100644 index 0000000..ad0e9bf --- /dev/null +++ b/OU4/is_connected_backup.c @@ -0,0 +1,175 @@ +#include +#include +#include // for EXIT_FAILURE +#include +#include +#include +#include + + +#define MAXNODENAME 40 +#define BUFSIZE 400 + + +// ===========HELP FUNCTIONS ============ +/** + * @brief Find position of first non-whitespace character. + * + * @param s - string to search. + * @return int - Returns the position of the first non-whitespace character, or -1 if not found. + */ +int first_non_white_space(const char *s) +{ + int i = 0; // Start at first char. + + // Advance as long as we're loooking at white-space until we hit EOL. + while (s[i] && isspace(s[i])) { + i++; + } + // We found a non-whitespace char. + if (s[i]) { + // It was a proper characted. Return its position. + return i; + } else { + // It was the null terminator. Return fail. + return -1; + } +} + +/** + * @brief Determine if the string is blank. + * + * @param s - string to search. + * @return true if the line contains only whitespace chars. + * @return false if the line contains at least one non-whitespace char. + */ + +bool line_is_blank(const char *s) +{ + // Line is blank if it only contained white-space chars. + return first_non_white_space(s) < 0; +} + +/** + * @brief Determina if the string is a comment line. + * + * @param s - string to search. + * @return true if the line is a comment line. + * @return false if the line is not a comment line. + * + * A comment line has a hash sign '#' as the first non-whitespace char on the line. + */ +bool line_is_comment(const char *s) +{ + int i = first_non_white_space(s); + return (i >= 0 && s[i] == '#'); +} + +/** + * @brief Extract node names from a line from the map file. + * + * @param buf - Input buffer. + * @param n1 - Output buffer for the first node name. Must be at least MAXNODENAME+1 long. + * @param n2 - Ditto for the second node name. + * @return int - Returns the number of correctly parsed node names. If the return value is 2, both n1 + * and n2 contain node names. If the return value is less than 2, parsing of at least one node name + * failed, in which case the content of n1 and n2 are undefined. + */ +int parse_map_line(const char *buf, char *n1, char *n2) +{ + // Create a format string the will do the work. + char fmt[20]; + // This will generate the format string " %40s %40s" if MAXNODENAME is 40. + snprintf(fmt, sizeof(fmt), " %%%ds %%%ds", MAXNODENAME, MAXNODENAME); + + // sscanf does all the necessary parsing. + // Node names must be separated by whitespace. + // Whitespace before the first node name is allowed. + // Anything after the second node name is ignored. + int n = sscanf(buf, fmt, n1, n2); + + // The return value from sscanf contains the number of properly parsed format codes, i.e. the + // number of node names. + return n; +} + +/** + * FUNCTION TO TRAVERSE THE GRAPH + * @g: pointer to the graph + * @src: pointer to source node + * @dest: pointer to destination node + */ +bool find_path(graph *g, node *src, node *dest) +{ + //Mark the starting node as seen + g = graph_node_set_seen(g,src,true); + + + //Put it in an empty queue + queue *q = queue_enqueue(queue_empty(NULL),src); + while (!queue_is_empty(q)) + { + //pick the first node from queue + src = queue_front(q); + q = queue_dequeue(q); + + //if node equals destinatiom node, return true + if (nodes_are_equal(src,dest)) + { + return true; + } + //get the neighbours + dlist *neighbours = graph_neighbours(g, src); + dlist_pos p = dlist_first(neighbours); + + while (!dlist_is_end(neighbours,p)) + { + + if (!graph_node_is_seen(g,p)) + { + //Mark unseen node as seen and put in queue + g = graph_node_set_seen(g,dlist_inspect(neighbours,p),true); + q = queue_enqueue(q,dlist_inspect(neighbours,p)); + } + p = dlist_next(neighbours,p); + } + + } + + return false; +} + +/** + * FUNCTION THAT MAKES A GRAPH FROM FILE + * + */ +graph *graph_from_file() +{ + FILE *fptr = fopen("airmap1.map", "r"); + graph *g; + if (fptr == NULL) + { + printf("Error reading the file\n"); + exit(EXIT_FAILURE); + } + + char string[100]; + + +} + + //MAIN +int main(void) +{ + + printf("Enter origin and destination (quit to exit): "); + + return 0; +} + + + + + + + diff --git a/OU4/is_connected_help_functions.c b/OU4/is_connected_help_functions.c new file mode 100644 index 0000000..e0b7f5e --- /dev/null +++ b/OU4/is_connected_help_functions.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +#define MAXNODENAME 40 +#define BUFSIZE 400 + +/** + * @brief Find position of first non-whitespace character. + * + * @param s - string to search. + * @return int - Returns the position of the first non-whitespace character, or -1 if not found. + */ +int first_non_white_space(const char *s) +{ + int i = 0; // Start at first char. + + // Advance as long as we're loooking at white-space until we hit EOL. + while (s[i] && isspace(s[i])) { + i++; + } + // We found a non-whitespace char. + if (s[i]) { + // It was a proper characted. Return its position. + return i; + } else { + // It was the null terminator. Return fail. + return -1; + } +} + +/** + * @brief Determine if the string is blank. + * + * @param s - string to search. + * @return true if the line contains only whitespace chars. + * @return false if the line contains at least one non-whitespace char. + */ + +bool line_is_blank(const char *s) +{ + // Line is blank if it only contained white-space chars. + return first_non_white_space(s) < 0; +} + +/** + * @brief Determina if the string is a comment line. + * + * @param s - string to search. + * @return true if the line is a comment line. + * @return false if the line is not a comment line. + * + * A comment line has a hash sign '#' as the first non-whitespace char on the line. + */ +bool line_is_comment(const char *s) +{ + int i = first_non_white_space(s); + return (i >= 0 && s[i] == '#'); +} + +/** + * @brief Extract node names from a line from the map file. + * + * @param buf - Input buffer. + * @param n1 - Output buffer for the first node name. Must be at least MAXNODENAME+1 long. + * @param n2 - Ditto for the second node name. + * @return int - Returns the number of correctly parsed node names. If the return value is 2, both n1 + * and n2 contain node names. If the return value is less than 2, parsing of at least one node name + * failed, in which case the content of n1 and n2 are undefined. + */ +int parse_map_line(const char *buf, char *n1, char *n2) +{ + // Create a format string the will do the work. + char fmt[20]; + // This will generate the format string " %40s %40s" if MAXNODENAME is 40. + snprintf(fmt, sizeof(fmt), " %%%ds %%%ds", MAXNODENAME, MAXNODENAME); + + // sscanf does all the necessary parsing. + // Node names must be separated by whitespace. + // Whitespace before the first node name is allowed. + // Anything after the second node name is ignored. + int n = sscanf(buf, fmt, n1, n2); + + // The return value from sscanf contains the number of properly parsed format codes, i.e. the + // number of node names. + return n; +} diff --git a/OU4/marctest b/OU4/marctest new file mode 100644 index 0000000..c5d6f79 Binary files /dev/null and b/OU4/marctest differ diff --git a/OU4/marctest.c b/OU4/marctest.c new file mode 100644 index 0000000..561950e --- /dev/null +++ b/OU4/marctest.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include "graph.h" + +// Helper function to print test results (Sonet Thinking Generated) +void test_result(const char *test_name, bool result) { + printf("%-40s %s\n", test_name, result ? "PASSED" : "FAILED"); +} +/*void print_string(const void *s) +{ + const char *string = s; + printf("[%s] ", string); +} + */ +int main(void) +{ + printf("Testing graph implementation...\n\n"); + int max_nodes = 10; + // Test 1: Create an empty graph + graph *g = graph_empty(max_nodes); + test_result("Create empty graph", g != NULL); + + // Test 2: Check if graph is empty + test_result("Graph is empty", graph_is_empty(g)); + + // Test 3: Insert nodes + char *node1 = "AAA"; + char *node2 = "BBB"; + char *node3 = "CCC"; + + g = graph_insert_node(g, node1); + test_result("Insert node A", !graph_is_empty(g)); + + g = graph_insert_node(g, node2); + g = graph_insert_node(g, node3); + + // Test 4: Find nodes + node *n1 = graph_find_node(g, node1); + test_result("Find node A", n1 != NULL); + + node *n2 = graph_find_node(g, node2); + test_result("Find node B", n2 != NULL); + + node *n3 = graph_find_node(g, node3); + test_result("Find node C", n3 != NULL); + + node *n4 = graph_find_node(g, "D"); + test_result("Find non-existent node D", n4 == NULL); + + // Test 5: Insert edges + g = graph_insert_edge(g, n1, n2); + g = graph_insert_edge(g, n1, n3); + g = graph_insert_edge(g, n2, n1); + g = graph_insert_edge(g, n2, n2); + g = graph_insert_edge(g, n1, n1); + + test_result("Graph has edges", graph_has_edges(g)); + + // Test 6: Node seen status + test_result("Node A not seen initially", !graph_node_is_seen(g, n1)); + + g = graph_node_set_seen(g, n1, true); + test_result("Node A is seen after setting", graph_node_is_seen(g, n1)); + + g = graph_node_set_seen(g, n2, true); + test_result("Node B is seen after setting", graph_node_is_seen(g, n2)); + + g = graph_reset_seen(g); + test_result("Node A not seen after reset", !graph_node_is_seen(g, n1)); + test_result("Node B not seen after reset", !graph_node_is_seen(g, n2)); + + // Test 7: Neighbors (if implemented) + /* + dlist *neighbours = graph_neighbours(g, n1); + if (neighbours != NULL) { + printf("Neighbors of A: "); + // Print neighbors + //dlist_print(neighbours, print_string); + printf("\n\n"); + dlist_kill(neighbours); + }*/ + + // Test 8: Print the graph + graph_print(g); + + // Test 9: Clean up (if implemented) + graph_kill(g); + printf("Graph destroyed\n"); + + + printf("\nTests completed.\n"); + return 0; +} \ No newline at end of file diff --git a/OU4/marctest2 b/OU4/marctest2 new file mode 100644 index 0000000..a8b2ef3 Binary files /dev/null and b/OU4/marctest2 differ diff --git a/OU4/queue.h b/OU4/queue.h new file mode 100644 index 0000000..1070e98 --- /dev/null +++ b/OU4/queue.h @@ -0,0 +1,131 @@ +#ifndef __QUEUE_H +#define __QUEUE_H + +#include +#include "util.h" + +/* + * Declaration of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores void pointers, so it can be used to + * store all types of values. After use, the function queue_kill must + * be called to de-allocate the dynamic memory used by the queue + * itself. The de-allocation of any dynamic memory allocated for the + * element values is the responsibility of the user of the queue, + * unless a kill_function is registered in queue_empty. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation. + */ + +// ==========PUBLIC DATA TYPES============ + +// Queue type. +typedef struct queue queue; + +// ==========DATA STRUCTURE INTERFACE========== + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func); + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q); + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v); + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q); + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q); + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q); + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func); + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level); + +#endif diff --git a/OU4/rapport_ou4.pdf b/OU4/rapport_ou4.pdf new file mode 100644 index 0000000..cc15f24 Binary files /dev/null and b/OU4/rapport_ou4.pdf differ diff --git a/OU4/src/Makefile b/OU4/src/Makefile new file mode 100644 index 0000000..e3d0733 --- /dev/null +++ b/OU4/src/Makefile @@ -0,0 +1,13 @@ +# List of directories containing sub-projects +SUB_DIRS := array_1d array_2d dlist int_array_1d int_list int_list_array int_stack list int_queue queue stack table + +# Rule to call 'make all' in each sub-directory +all: + for dir in $(SUB_DIRS); do \ + $(MAKE) -C $$dir all; \ + done + +clean: + for dir in $(SUB_DIRS); do \ + $(MAKE) -C $$dir clean; \ + done diff --git a/OU4/src/array_1d/Makefile b/OU4/src/array_1d/Makefile new file mode 100644 index 0000000..ad8fc86 --- /dev/null +++ b/OU4/src/array_1d/Makefile @@ -0,0 +1,49 @@ +MWE = array_1d_mwe1 array_1d_mwe2 array_1d_mwe3 array_1d_mwe4 array_1d_mwe1i array_1d_mwe2i + +SRC = array_1d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +array_1d_mwe1: array_1d_mwe1.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe2: array_1d_mwe2.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe3: array_1d_mwe3.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe4: array_1d_mwe4.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe1i: array_1d_mwe1i.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +array_1d_mwe2i: array_1d_mwe2i.c array_1d.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: array_1d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: array_1d_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: array_1d_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: array_1d_mwe4 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/array_1d/array_1d.c b/OU4/src/array_1d/array_1d.c new file mode 100644 index 0000000..6244fac --- /dev/null +++ b/OU4/src/array_1d/array_1d.c @@ -0,0 +1,491 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-05-10: Added print_internal. + */ + +// =========== INTERNAL DATA TYPES ============ + +struct array_1d { + int low; // Low index limit. + int high; // High index limit. + int array_size; // Number of array elements. + void **values; // Pointer to where the actual values are stored. + kill_function kill_func; +}; + +// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_1d *array_1d_create(int lo, int hi, kill_function kill_func) +{ + // Allocate array structure. + array_1d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low = lo; + a->high = hi; + + // Number of elements. + a->array_size = hi - lo + 1; + + // Store kill function. + a->kill_func = kill_func; + + a->values = calloc(a->array_size, sizeof(void *)); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int array_1d_low(const array_1d *a) +{ + return a->low; +} + +/** + * array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int array_1d_high(const array_1d *a) +{ + return a->high; +} + +/** + * array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_1d_inspect_value(const array_1d *a, int i) +{ + int offset = i - array_1d_low(a); + // Return the value. + return a->values[offset]; +} + +/** + * array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_1d_has_value(const array_1d *a, int i) +{ + int offset = i - array_1d_low(a); + // Return true if the value is not NULL. + return a->values[offset] != NULL; +} + +/** + * array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_1d_set_value(array_1d *a, void *v, int i) +{ + int offset = i - array_1d_low(a); + // Call kill_func if specified and old element value was non-NULL. + if (a->kill_func != NULL && a->values[offset] != NULL) { + a->kill_func( a->values[offset] ); + } + // Set value. + a->values[offset] = v; +} + +/** + * array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_1d_kill(array_1d *a) +{ + if (a->kill_func != NULL) { + // Return user-allocated memory for each non-NULL element. + for (int i = 0; i < a->array_size; i++) { + if (a->values[i] != NULL) { + a->kill_func(a->values[i]); + } + } + } + // Return memory for value pointers. + free(a->values); + // Return memory for array structure. + free(a); +} + +/** + * array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_1d_print(const array_1d *a, inspect_callback print_func) +{ + printf("[ "); + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + printf("["); + print_func(array_1d_inspect_value(a, i)); + printf("]"); + } else { + printf(" []"); + } + if (i < array_1d_high(a)) { + printf(", "); + } + } + printf(" ]\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY array_1d_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|low\\n%d|high\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(a), PTR2ADDR(a->kill_func), a->low, a->high, a->array_size, + PTR2ADDR(a->values)); +} + +void print_values(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int i=0; i < a->array_size; i++) { + printf("<%02d>%d\\n%02d\\n%04lx", i, i + a->low, i, PTR2ADDR(a->values[i])); + if (i < a->array_size - 1) { + printf("|"); + } + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const array_1d *a) +{ + iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a)); + if (a->values == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(a->values)); + } + printf(" [label=\"values\"]\n"); +} + +// Print nodes for each value memory block +static void print_value_nodes(int indent_level, const array_1d *a, inspect_callback print_func) +{ + for (int i=0; i <= a->array_size; i++) { + if (a->values[i] != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i])); + if (print_func != NULL) { + print_func(a->values[i]); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i])); + } + } +} + +// Print edges from each value pointer to payload memory +static void print_value_edges(int indent_level, const array_1d *a) +{ + for (int i=0; i < a->array_size; i++) { + // Buffer to store port name in. Good for array up to 1e9 elements. + char port[15]; + // Create port name + sprintf(port, "%02d", i); + if (a->kill_func) { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red"); + } else { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red style=dashed"); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * array_1d_print_internal() - Print the internal structure of the array in dot format. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_1D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_array_1d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array + print_values(il, a); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Print nodes for each value memory block + print_value_nodes(il, a, print_func); + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, a); + + // Print edges from each value pointer to payload memory + print_value_edges(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/array_1d/array_1d_mwe1.c b/OU4/src/array_1d/array_1d_mwe1.c new file mode 100644 index 0000000..5664909 --- /dev/null +++ b/OU4/src/array_1d/array_1d_mwe1.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * Removed use of automatic, magic, free_handler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + * v1.31 2024-04-05: Bugfix: Remove call to array_1d_print_internal + * that shouldn't be here. + */ + +#define VERSION "v1.31" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, NULL); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a, v, i); + } + printf("Array after inserting i^2, i=1,...,6:\n"); + array_1d_print(a, print_int); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + int *v = array_1d_inspect_value(a, i); + int_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_1d/array_1d_mwe1i.c b/OU4/src/array_1d/array_1d_mwe1i.c new file mode 100644 index 0000000..fba94d1 --- /dev/null +++ b/OU4/src/array_1d/array_1d_mwe1i.c @@ -0,0 +1,146 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-05: Adapted from array_1d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_1d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, NULL); + + printf("Empty array from the outside:\n"); + array_1d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + printf("Array from the outside after setting half the values:\n"); + array_1d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting 3 values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_1d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_1d_high(a) ; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + + printf("Array from the outside after setting all the values:\n"); + array_1d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all 6 values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_1d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a, i)) { + int *v=array_1d_inspect_value(a,i); + int_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_1d/array_1d_mwe2.c b/OU4/src/array_1d/array_1d_mwe2.c new file mode 100644 index 0000000..04b002f --- /dev/null +++ b/OU4/src/array_1d/array_1d_mwe2.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 2 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, int_kill); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a, v, i); + } + printf("Array after inserting i^2, i=1,...,6:\n"); + array_1d_print(a, print_int); + + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_1d/array_1d_mwe2i.c b/OU4/src/array_1d/array_1d_mwe2i.c new file mode 100644 index 0000000..9462bb8 --- /dev/null +++ b/OU4/src/array_1d/array_1d_mwe2i.c @@ -0,0 +1,140 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-05: Adapted from array_1d_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-05" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_1d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + array_1d *a = array_1d_create(1, 6, int_kill); + + printf("Empty array from the outside:\n"); + array_1d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + printf("Array from the outside after setting half the values:\n"); + array_1d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting 3 values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_1d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_1d_high(a) ; i++) { + // Allocate memory for an integer. + int *v=int_create(i * i); + array_1d_set_value(a,v,i); + } + + printf("Array from the outside after setting all the values:\n"); + array_1d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all 6 values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_1d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Clean up the array. + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_1d/array_1d_mwe3.c b/OU4/src/array_1d/array_1d_mwe3.c new file mode 100644 index 0000000..318de8f --- /dev/null +++ b/OU4/src/array_1d/array_1d_mwe3.c @@ -0,0 +1,99 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 3 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * Removed use of automatic, magic, free_handler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Struct with month name and number of days. +typedef struct month { + char *name; + int days; +} month; + +// Months are stored via void pointers. Convert the given pointer and +// print the dereferenced values. +void print_month(const void *data) +{ + // Convert void pointer to pointer to month. + const month *m=data; + printf("(%s, %d)", m->name, m->days); +} + +// Allocate space for and populate a month structure +month *month_create(const char *name, int days) +{ + // Allocate memory for a month structure. + month *m = malloc(sizeof(*m)); + // Allocate memory for the month name. + m->name = calloc(strlen(name) + 1, sizeof(char)); + // Copy the string. + strcpy(m->name, name); + // Set days. + m->days = days; + return m; +} + +// Function to free both month structure and char * with name. +void month_kill(void *data) +{ + // Convert void pointer to pointer to month. + month *m=data; + free(m->name); + free(m); +} + +int main(void) +{ + printf("%s, %s %s: Create month array without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + const char *month_names[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int month_days[12] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + // Create an array with 12 positions. + array_1d *a = array_1d_create(1, 12, NULL); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate and populate a month structure. + month *m=month_create(month_names[i - 1], month_days[i - 1]); + // Set value in array. + array_1d_set_value(a,m,i); + } + printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n"); + array_1d_print(a, print_month); + + // Empty the array. + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + if (array_1d_has_value(a,i)) { + month *v=array_1d_inspect_value(a,i); + month_kill(v); + } + } + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_1d/array_1d_mwe4.c b/OU4/src/array_1d/array_1d_mwe4.c new file mode 100644 index 0000000..24ed388 --- /dev/null +++ b/OU4/src/array_1d/array_1d_mwe4.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 4 for array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into simpler, shorter versions. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Struct with month name and number of days. +typedef struct month { + char *name; + int days; +} month; + +// Months are stored via void pointers. Convert the given pointer and +// print the dereferenced values. +void print_month(const void *data) +{ + // Convert void pointer to pointer to month. + const month *m=data; + printf("(%s, %d)", m->name, m->days); +} + +// Allocate space for and populate a month structure +month *month_create(const char *name, int days) +{ + // Allocate memory for a month structure. + month *m = malloc(sizeof(*m)); + // Allocate memory for the month name. + m->name = calloc(strlen(name) + 1, sizeof(char)); + // Copy the string. + strcpy(m->name, name); + // Set days. + m->days = days; + return m; +} + +// Function to free both month structure and char * with name. +void month_kill(void *data) +{ + // Convert void pointer to pointer to month. + month *m=data; + free(m->name); + free(m); +} + +int main(void) +{ + printf("%s, %s %s: Create month array with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + const char *month_names[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + int month_days[12] = { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + + // Create an array with 12 positions. + array_1d *a = array_1d_create(1, 12, month_kill); + + for (int i=array_1d_low(a); i<=array_1d_high(a); i++) { + // Allocate and populate a month structure. + month *m=month_create(month_names[i - 1], month_days[i - 1]); + // Set value in array. + array_1d_set_value(a,m,i); + } + printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n"); + array_1d_print(a, print_month); + + array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_2d/Makefile b/OU4/src/array_2d/Makefile new file mode 100644 index 0000000..e64cc6a --- /dev/null +++ b/OU4/src/array_2d/Makefile @@ -0,0 +1,37 @@ +MWE = array_2d_mwe1 array_2d_mwe2 array_2d_mwe1i array_2d_mwe2i + +SRC = array_2d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +array_2d_mwe1: array_2d_mwe1.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe2: array_2d_mwe2.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe1i: array_2d_mwe1i.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +array_2d_mwe2i: array_2d_mwe2i.c array_2d.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: array_2d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: array_2d_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/array_2d/array_2d.c b/OU4/src/array_2d/array_2d.c new file mode 100644 index 0000000..2c10938 --- /dev/null +++ b/OU4/src/array_2d/array_2d.c @@ -0,0 +1,526 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 2D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Moved freehandler to last in create parameter list. + * v1.2 2024-05-10: Added print_internal. + * v1.21 2024-05-15: Bugfix to get rid of sprintf warning. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct array_2d { + int low[2]; // Low index limits. + int high[2]; // High index limits. + int array_size; // Number of array elements. + void **values; // Pointer to where the actual values are stored. + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * array_2d_create() - Create an array without values. + * @lo1: low index limit for first dimension. + * @hi1: high index limit for first dimension. + * @lo2: low index limit for second dimension. + * @hi2: high index limit for second dimension. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2, + kill_function kill_func) +{ + // Allocate array structure. + array_2d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low[0] = lo1; + a->low[1] = lo2; + a->high[0] = hi1; + a->high[1] = hi2; + + // Number of elements. + a->array_size = (hi1 - lo1 + 1) * (hi2 - lo2 + 1); + + // Store kill function. + a->kill_func = kill_func; + + a->values=calloc(a->array_size, sizeof(void *)); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * array_2d_low() - Return the low index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The low index limit for dimension number d. + */ +int array_2d_low(const array_2d *a, int d) +{ + return a->low[d - 1]; +} + +/** + * array_2d_high() - Return the high index limit for the array. + * @a: array to inspect. + * @d: dimension number, 1 or 2. + * + * Returns: The high index limit for dimension number d. + */ +int array_2d_high(const array_2d *a, int d) +{ + return a->high[d - 1]; +} + +/** + * array_2d_linear_index() - Internal function to compute linear index from list + * of indices. + * @a: array to inspect. + * @i: First index. + * @j: Second index. + * + * Returns: The linear index corresponding to the list of indices. + * NOTE: The result is undefined if the number of arguments + * or index value are out of bounds. + */ +static int array_2d_linear_index(const array_2d *a, int i, int j) +{ + int rows = a->high[0] - a->low[0] + 1; + int ix=(i - a->low[0]) + (j - a->low[1]) * rows; + return ix; +} + +/** + * array_2d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: First index of position to inspect. + * @j: Second index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +void *array_2d_inspect_value(const array_2d *a, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Return the value. + return a->values[ix]; +} + +/** + * array_2d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: First index of position to inspect. + * @j: Second index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool array_2d_has_value(const array_2d *a, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Return true if the value is not NULL. + return a->values[ix] != NULL; +} + +/** + * array_2d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or NULL to clear value. + * @i: First index of position to modify. + * @j: Second index of position to modify. + * + * If the old element value is non-NULL, calls kill_func if it was + * specified at array creation. + * + * Returns: Nothing. + */ +void array_2d_set_value(array_2d *a, void *v, int i, int j) +{ + int ix = array_2d_linear_index(a, i, j); + // Call kill_func if specified and old element value was non-NULL. + if (a->kill_func != NULL && a->values[ix] != NULL) { + a->kill_func(a->values[ix]); + } + // Set value. + a->values[ix]=v; +} + +/** + * array_2d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Iterates over all elements. If kill_func was specified at array + * creation, calls it for every non-NULL element value. + * + * Returns: Nothing. + */ +void array_2d_kill(array_2d *a) +{ + if (a->kill_func) { + // Return user-allocated memory for each non-NULL element. + for (int i=0; iarray_size; i++) { + if (a->values[i] != NULL) { + a->kill_func(a->values[i]); + } + } + } + // Free actual storage. + free(a->values); + // Free array structure. + free(a); +} + +/** + * array_2d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void array_2d_print(const array_2d *a, inspect_callback print_func) +{ + printf("[\n"); + for (int i=array_2d_low(a,1); i<=array_2d_high(a,1); i++) { + printf(" [ "); + for (int j=array_2d_low(a,2); j<=array_2d_high(a,2); j++) { + if (array_2d_has_value(a,i,j)) { + printf("["); + print_func(array_2d_inspect_value(a,i,j)); + printf("]"); + } else { + printf("[ ]"); + } + if (j ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const array_2d *p) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"func\\n%04lx|low[0]\\n%d|high[0]\\n%d" + "|low[1]\\n%d|high[1]\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->kill_func), p->low[0], p->high[0], + p->low[1], p->high[1], p->array_size, PTR2ADDR(p->values)); +} + +static void print_values(int indent_level, const array_2d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int j=array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + for (int i=array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + int li=array_2d_linear_index(a, i, j); + printf("<%02d>(%d,%d)\\n%02d\\n%04lx", + li, i, j, li, PTR2ADDR(a->values[li])); + if (li < a->array_size - 1) { + printf("|"); + } + } + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const array_2d *a) +{ + iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a)); + if (a->values == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(a->values)); + } + printf(" [label=\"values\"]\n"); +} + +// Print nodes for each value memory block +static void print_value_nodes(int indent_level, const array_2d *a, inspect_callback print_func) +{ + for (int i=0; i <= a->array_size; i++) { + if (a->values[i] != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i])); + if (print_func != NULL) { + print_func(a->values[i]); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i])); + } + } +} + +// Print edges from each value pointer to payload memory +static void print_value_edges(int indent_level, const array_2d *a) +{ + for (int i=0; i < a->array_size; i++) { + // Buffer to store port name in. Good for array up to 1e9 elements. + char port[15]; + // Create port name + sprintf(port, "%02d", i); + if (a->kill_func) { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red"); + } else { + print_edge(indent_level, a->values, a->values[i], port, port, + "color=red style=dashed"); + } + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * array_2d_print_internal() - Print the arrays internal structure. + * @a: Array to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_2D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_array_2d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array + print_values(il, a); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Print nodes for each value memory block + print_value_nodes(il, a, print_func); + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, a); + + // Print edges from each value pointer to payload memory + print_value_edges(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/array_2d/array_2d_mwe1.c b/OU4/src/array_2d/array_2d_mwe1.c new file mode 100644 index 0000000..4261433 --- /dev/null +++ b/OU4/src/array_2d/array_2d_mwe1.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2023-01-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, NULL); + + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("After filling the array with values:\n"); + array_2d_print(a, print_int); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_2d/array_2d_mwe1i.c b/OU4/src/array_2d/array_2d_mwe1i.c new file mode 100644 index 0000000..6a600e7 --- /dev/null +++ b/OU4/src/array_2d/array_2d_mwe1i.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-07: Adapted from array_2d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-07" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_2d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_2d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, NULL); + + printf("Empty array from the outside:\n"); + array_2d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("Array from the outside after setting half the values:\n"); + array_2d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting half of the values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_2d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + + printf("Array from the outside after setting all the values:\n"); + array_2d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all the values.\n" + "Red lines are used for the array payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the array, i.e., the payload\n" + "memory will NOT be deallocated by the array.\n\n" + "See array_2d_mwe2i for an array example\nthat owns the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_2d/array_2d_mwe2.c b/OU4/src/array_2d/array_2d_mwe2.c new file mode 100644 index 0000000..bc48a0f --- /dev/null +++ b/OU4/src/array_2d/array_2d_mwe2.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2023-01-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, int_kill); + + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("After filling the array with values:\n"); + array_2d_print(a, print_int); + + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/array_2d/array_2d_mwe2i.c b/OU4/src/array_2d/array_2d_mwe2i.c new file mode 100644 index 0000000..ecb1167 --- /dev/null +++ b/OU4/src/array_2d/array_2d_mwe2i.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include + +/* + * Minimum working example for array_2d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-07: Adapted from array_2d_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-07" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const array_2d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + array_2d_print_internal(a, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create a 4-by-3 array. + array_2d *a = array_2d_create(1, 4, 1, 3, int_kill); + + printf("Empty array from the outside:\n"); + array_2d_print(a, print_int); + print_internal_with_cut_lines(a, "Empty array showing the internal structure"); + + int i; + + // Set values of 50% of the elements + for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + printf("Array from the outside after setting half the values:\n"); + array_2d_print(a, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Array after setting half of the values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_2d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc); + + // Set the rest of the element values. + // Note: The empty initialization is on purpose. + for ( ; i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + // Allocate memory for an integer. + int *v=int_create(i * 10 + j); + array_2d_set_value(a, v, i, j); + } + } + + printf("Array from the outside after setting all the values:\n"); + array_2d_print(a, print_int); + const char *long_desc2 = __FILE__ + ": Internal structure of the Array after setting all the values.\n" + "Red lines are used for the array payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the array, i.e., the payload\n" + "memory WILL be deallocated by the array.\n\n" + "See array_2d_mwe1i for an array example\nthat borrows the payload memory."; + print_internal_with_cut_lines(a, long_desc2); + + // Empty the array. + for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) { + for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) { + if (array_2d_has_value(a, i, j)) { + int *v=array_2d_inspect_value(a, i, j); + int_kill(v); + } + } + } + // Return remaining memory. + array_2d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/dlist/Makefile b/OU4/src/dlist/Makefile new file mode 100644 index 0000000..914da3e --- /dev/null +++ b/OU4/src/dlist/Makefile @@ -0,0 +1,42 @@ +MWE = dlist_mwe1 dlist_mwe2 dlist_mwe1i dlist_mwe2i + +SRC = dlist.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +clean: + -rm -f $(MWE) $(OBJ) + +dlist_mwe1: dlist_mwe1.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe2: dlist_mwe2.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe1i: dlist_mwe1i.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +dlist_mwe2i: dlist_mwe2i.c dlist.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: dlist_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: dlist_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: dlist_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: dlist_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/dlist/README.md b/OU4/src/dlist/README.md new file mode 100644 index 0000000..85f7b69 --- /dev/null +++ b/OU4/src/dlist/README.md @@ -0,0 +1,9 @@ +# Riktad lista + +En implementation av ADTn _Riktad Lista_ baserad på en enkel-länkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + diff --git a/OU4/src/dlist/dlist.c b/OU4/src/dlist/dlist.c new file mode 100644 index 0000000..414c8dc --- /dev/null +++ b/OU4/src/dlist/dlist.c @@ -0,0 +1,588 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic, undirected list for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added dlist_pos_are_equal and dlist_pos_is_valid functions. + * v1.2 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal. + * v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list elements are implemented as one-cells with a forward link. + * The list position is a pointer to the internal cell before the cell + * with the value. + */ +typedef struct cell { + struct cell *next; + void *val; +} cell; + + +struct dlist { + cell *head; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * dlist_empty() - Create an empty dlist. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +dlist *dlist_empty(kill_function kill_func) +{ + // Allocate memory for the list structure. + dlist *l = calloc(1, sizeof(*l)); + + // Allocate memory for the list head. + l->head = calloc(1, sizeof(cell)); + + // No elements in list so far. + l->head->next = NULL; + + // Store the kill function. + l->kill_func = kill_func; + + return l; +} + +/** + * dlist_is_empty() - Check if a dlist is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool dlist_is_empty(const dlist *l) +{ + return (l->head->next == NULL); +} + +/** + * dlist_first() - Return the first position of a dlist, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +dlist_pos dlist_first(const dlist *l) +{ + return l->head; +} + +/** + * dlist_next() - Return the next position in a dlist. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +dlist_pos dlist_next(const dlist *l, const dlist_pos p) +{ + if (dlist_is_end(l, p)) { + // This should really throw an error. + fprintf(stderr,"dlist_next: Warning: Trying to navigate " + "past end of list!"); + } + return p->next; +} + +/** + * dlist_is_end() - Check if a given position is at the end of a dlist. + * @l: List to inspect. + * @p: Any valid position in the list. + * + * Returns: True if p is at the end of the list. + */ +bool dlist_is_end(const dlist *l, const dlist_pos p) +{ + return p->next == NULL; +} + +/** + * dlist_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: Returns the value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *dlist_inspect(const dlist *l, const dlist_pos p) +{ + if (dlist_is_end(l, p)) { + // This should really throw an error. + fprintf(stderr,"dlist_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->next->val; +} + +/** + * dlist_insert() - Insert a new element with a given value into a dlist. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p) +{ + // Create new element. + dlist_pos new_pos=calloc(1, sizeof(cell)); + // Set value. + new_pos->val=v; + + // Set links. + new_pos->next=p->next; + p->next=new_pos; + + return p; +} + +/** + * dlist_remove() - Remove an element from a dlist. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +dlist_pos dlist_remove(dlist *l, const dlist_pos p) +{ + // Cell to remove. + dlist_pos c=p->next; + + // Link past cell to remove. + p->next=c->next; + + // Call kill_func if registered. + if(l->kill_func != NULL) { + // Return any user-allocated memory for the value. + l->kill_func(c->val); + } + // Free the memory allocated to the cell itself. + free(c); + // Return the position of the next element. + return p; +} + +/** + * dlist_kill() - Destroy a given dlist. + * @l: List to destroy. + * + * Return all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void dlist_kill(dlist *l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + dlist_pos p = dlist_first(l); + + // Remove first element until list is empty. + while(!dlist_is_empty(l)) { + p = dlist_remove(l, p); + } + + // Free the head and the list itself. + free(l->head); + free(l); +} + +/** + * dlist_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void dlist_print(const dlist *l, inspect_callback print_func) +{ + // Start at the beginning of the list. + dlist_pos p = dlist_first(l); + + printf("( "); + while (!dlist_is_end(l, p)) { + // Call print_func with the element value at the + // current position. + print_func(dlist_inspect(l, p)); + // Advance to next position. + p = dlist_next(l, p); + // Print separator unless at element. + if (!dlist_is_end(l, p)) { + printf(", "); + } + } + printf(" )\n"); +} + +/** + * dlist_pos_is_equal() - Return true if two positions in a dlist are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * dlist_pos_is_valid() - Return true for a valid position in a dlist. + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool dlist_pos_is_valid(const dlist *l, const dlist_pos p) +{ + // Iterate over all positions in l. + dlist_pos q = dlist_first(l); + while (!dlist_is_end(l, q)) { + if (dlist_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = dlist_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the dlist head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const dlist *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|head\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l)); +} + +// Print edges from the list head to the head cell. +static void print_head_edges(int indent_level, const dlist *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const dlist_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%04lx|next\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const dlist *l, const dlist_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + + // Ignore val ptr for head and tail nodes. + if (p == l->head) { + return; + } + if (l->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * dlist_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph DLIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_dlist_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_dlist_%d { label=\"DList\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + dlist_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = l->head; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/dlist/dlist_mwe1.c b/OU4/src/dlist/dlist_mwe1.c new file mode 100644 index 0000000..2f2d14b --- /dev/null +++ b/OU4/src/dlist/dlist_mwe1.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 1 for dlist.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit copy/kill string functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// Test program. +int main(void) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(NULL); + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList after inserting 6 strings:\n"); + dlist_print(l, print_string); + + // Traverse the list and deallocate each value. + p = dlist_first(l); + while (!dlist_is_end(l,p)) { + char *v=dlist_inspect(l,p); + kill_string(v); + p=dlist_remove(l,p); + } + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/dlist/dlist_mwe1i.c b/OU4/src/dlist/dlist_mwe1i.c new file mode 100644 index 0000000..fd9ff6d --- /dev/null +++ b/OU4/src/dlist/dlist_mwe1i.c @@ -0,0 +1,142 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for dlist.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "borrows" the payload memory, i.e., the user of the list is + * responsible for deallocating the payload memory. See dlist_mwe2i.c + * for a version where the list takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +void print_string_internal(const void *value) +{ + const char *s=value; + // We must double-escape the double quotes since the output will + // be parsed a second time by graphviz. + printf("\\\"%s\\\"", s); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const dlist *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + dlist_print_internal(l, print_string_internal, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +// Test program. +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(NULL); + + printf("Empty list from the outside:\n"); + dlist_print(l, print_string); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList from the outside after inserting 6 strings:\n"); + dlist_print(l, print_string); + const char *long_desc = __FILE__ + ": Internal structure of the DList after inserting 6 strings.\n" + "Red lines are used for the list payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the list, i.e., the payload\n" + "memory will NOT be deallocated by the list.\n\n" + "See dlist_mwe2i for a list example\nthat owns the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Traverse the list and deallocate each value. + p = dlist_first(l); + while (!dlist_is_end(l,p)) { + char *v=dlist_inspect(l,p); + kill_string(v); + p=dlist_remove(l,p); + } + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/dlist/dlist_mwe2.c b/OU4/src/dlist/dlist_mwe2.c new file mode 100644 index 0000000..0db799a --- /dev/null +++ b/OU4/src/dlist/dlist_mwe2.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include + +/* + * Minimum working example 2 for dlist.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit copy/kill string functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// Test program. +int main(void) +{ + printf("%s, %s %s: Create list of strings with standard memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(kill_string); + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList after inserting 6 strings:\n"); + dlist_print(l, print_string); + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/dlist/dlist_mwe2i.c b/OU4/src/dlist/dlist_mwe2i.c new file mode 100644 index 0000000..ceef4fc --- /dev/null +++ b/OU4/src/dlist/dlist_mwe2i.c @@ -0,0 +1,136 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for dlist.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "owns" the payload memory, i.e., the list takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See list_mwe1i.c for a version + * where the list does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Make a dynamic copy of the input string. +char *copy_string(char *str) +{ + char *copy = calloc(strlen(str) + 1, sizeof(char)); + strcpy(copy, str); + return copy; +} + +// Return the memory occupied by the string +void kill_string(void *v) +{ + char *p = v; // Convert to a char ptr, useful in a debugger + free(p); +} + +// String print function. +void print_string(const void *value) +{ + const char *s=value; + printf("\"%s\"", s); +} + +void print_string_internal(const void *value) +{ + const char *s=value; + // We must double-escape the double quotes since the output will + // be parsed a second time by graphviz. + printf("\\\"%s\\\"", s); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const dlist *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + dlist_print_internal(l, print_string_internal, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +// Test program. +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create list of strings without memfreehandler.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Names to insert in the list. + char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"}; + + // Create the list + dlist *l = dlist_empty(kill_string); + + printf("Empty list from the outside:\n"); + dlist_print(l, print_string); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + dlist_pos p = dlist_first(l); + for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + // Insert dynamic copies of strings at last position. + p = dlist_insert(l, copy_string(names[i]), p); + p = dlist_next(l, p); + } + // Print the list. + printf("DList from the outside after inserting 6 strings:\n"); + dlist_print(l, print_string); + const char *long_desc = __FILE__ + ": Internal structure of the DList after inserting 6 strings.\n" + "Red lines are used for the list payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the list, i.e., the payload\n" + "memory WILL be deallocated by the list.\n\n" + "See dlist_mwe1i for a list example\nthat borrows the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Destroy the list. + dlist_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_array_1d/Makefile b/OU4/src/int_array_1d/Makefile new file mode 100644 index 0000000..a0491dc --- /dev/null +++ b/OU4/src/int_array_1d/Makefile @@ -0,0 +1,28 @@ +MWE = int_array_1d_mwe1 int_array_1d_mwe1i + +SRC = int_array_1d.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_array_1d_mwe1: int_array_1d_mwe1.c int_array_1d.c + gcc -o $@ $(CFLAGS) $^ + +int_array_1d_mwe1i: int_array_1d_mwe1i.c int_array_1d.c + gcc -o $@ $(CFLAGS) $^ + +memtest: int_array_1d_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/int_array_1d/int_array_1d.c b/OU4/src/int_array_1d/int_array_1d.c new file mode 100644 index 0000000..9c606fe --- /dev/null +++ b/OU4/src/int_array_1d/int_array_1d.c @@ -0,0 +1,424 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic 1D array for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2024-05-10: Added print_internal. + */ + +// =========== INTERNAL DATA TYPES ============ + +struct int_array_1d { + int low; // Low index limit. + int high; // High index limit. + int array_size; // Number of array elements. + int *values; // Pointer to where the actual values are stored. +}; + +// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * int_array_1d_create() - Create an array without values. + * @lo: low index limit. + * @hi: high index limit. + * + * The index limits are inclusive, i.e. all indices i such that low <= + * i <= high are defined. + * + * Returns: A pointer to the new array, or NULL if not enough memory + * was available. + */ +int_array_1d *int_array_1d_create(int lo, int hi) +{ + // Allocate array structure. + int_array_1d *a = calloc(1, sizeof(*a)); + // Store index limit. + a->low = lo; + a->high = hi; + + // Number of elements. + a->array_size = hi - lo + 1; + + a->values=calloc(a->array_size, sizeof(a->values[0])); + + // Check whether the allocation succeeded. + if (a->values == NULL) { + free(a); + a = NULL; + } + return a; +} + +/** + * int_array_1d_low() - Return the low index limit for the array. + * @a: array to inspect. + * + * Returns: The low index limit. + */ +int int_array_1d_low(const int_array_1d *a) +{ + return a->low; +} + +/** + * int_array_1d_high() - Return the high index limit for the array. + * @a: array to inspect. + * + * Returns: The high index limit. + */ +int int_array_1d_high(const int_array_1d *a) +{ + return a->high; +} + +/** + * int_array_1d_inspect_value() - Inspect a value at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: The element value at the specified position. The result is + * undefined if no value are stored at that position. + */ +int int_array_1d_inspect_value(const int_array_1d *a, int i) +{ + int offset=i - int_array_1d_low(a); + // Return the value. + return a->values[offset]; +} + +/** + * int_array_1d_has_value() - Check if a value is set at a given array position. + * @a: array to inspect. + * @i: index of position to inspect. + * + * Returns: True if a value is set at the specified position, otherwise false. + */ +bool int_array_1d_has_value(const int_array_1d *a, int i) +{ + int offset=i - int_array_1d_low(a); + // Return true if the value is not 0. + return a->values[offset] != 0; +} + +/** + * int_array_1d_set_value() - Set a value at a given array position. + * @a: array to modify. + * @v: value to set element to, or 0 to clear value. + * @i: index of position to modify. + * + * Returns: Nothing. + */ +void int_array_1d_set_value(int_array_1d *a, int v, int i) +{ + int offset=i - int_array_1d_low(a); + // Set value. + a->values[offset] = v; +} + +/** + * int_array_1d_kill() - Return memory allocated by array. + * @a: array to kill. + * + * Returns: Nothing. + */ +void int_array_1d_kill(int_array_1d *a) +{ + // Free actual storage. + free(a->values); + // Free array structure. + free(a); +} + +/** + * int_array_1d_print() - Iterate over the array element and print their values. + * @a: Array to inspect. + * @print_func: Function called for each non-NULL element. + * + * Iterates over each position in the array. Calls print_func for each + * non-NULL value. + * + * Returns: Nothing. + */ +void int_array_1d_print(const int_array_1d *a) +{ + printf("[ "); + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + if (int_array_1d_has_value(a, i)) { + printf("[%d]", int_array_1d_inspect_value(a, i)); + } else { + printf(" []"); + } + if (i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the array head struct. + * @indent_level: Indentation level. + * @a: Array to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const int_array_1d *a) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"low\\n%d|high\\n%d" + "|array_size\\n%d|values\\n%04lx\"]\n", PTR2ADDR(a), + a->low, a->high, a->array_size, PTR2ADDR(a->values)); +} + +/** + * print_values() - Print a node containing all positions in the array. + * @indent_level: Indentation level. + * @a: Array to inspect. + * @max_values_to_print: Maximum number of values to output. + * + * Will print dot code to display each value in the array, up to + * max_values_to_print. If there are more values to print, adds an + * ellipsis (...) at the end. + * + * Returns: Nothing. + */ +void print_values(int indent_level, const int_array_1d *a, int max_values_to_print) +{ + int values_to_print = a->array_size; + int truncated = false; + if (values_to_print > max_values_to_print) { + values_to_print = max_values_to_print; + truncated = true; + } + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values)); + for (int i=0; i < values_to_print; i++) { + printf("%d\\n%02d\\n%d", i + a->low, i, a->values[i]); + if (i < values_to_print - 1) { + printf("|"); + } + } + if (truncated) { + // Add ellipsis + printf("|\\n...\\n"); + } + printf("\"]\n"); +} + +// Print edge from the array head to the values array. +static void print_head_edge(int indent_level, const int_array_1d *a) +{ + print_edge(indent_level, a, a->values, "v", "values", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * int_array_1d_print_internal() - Print the arrays internal structure in dot format. + * @a: Array to inspect. + * @desc: String with a description/state of the array, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the array and outputs dot code that shows the internal + * structure of the array. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph ARRAY_1D_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_int_array_1d_%d_description [label=\"%s\"]\n", graph_number, + escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a)); + iprintf(il, "a -> m%04lx\n", PTR2ADDR(a)); + } + + // Print the subgraph to surround the Array content + iprintf(il, "subgraph cluster_int_array_1d_%d { label=\"Array_1d\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, a); + + // Output the values array. Limit output to 20 elements. + print_values(il, a, 20); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, a); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/int_array_1d/int_array_1d_mwe1.c b/OU4/src/int_array_1d/int_array_1d_mwe1.c new file mode 100644 index 0000000..8f37f94 --- /dev/null +++ b/OU4/src/int_array_1d/int_array_1d_mwe1.c @@ -0,0 +1,38 @@ +#include +#include + +/* + * Minimum working example for int_array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-01-14" + +int main(void) +{ + printf("%s, %s %s: Create typed integer array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create an array with 6 positions. + int_array_1d *a = int_array_1d_create(1, 6); + + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + // Store square of index. + int_array_1d_set_value(a, i*i, i); + // Print array after setting each value. + printf("After setting value at index %d:\n", i); + int_array_1d_print(a); + } + + int_array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_array_1d/int_array_1d_mwe1i.c b/OU4/src/int_array_1d/int_array_1d_mwe1i.c new file mode 100644 index 0000000..44c0f9a --- /dev/null +++ b/OU4/src/int_array_1d/int_array_1d_mwe1i.c @@ -0,0 +1,83 @@ +#include +#include + +/* + * Minimum working example for int_array_1d.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-08: Adapted from int_array_1d_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-08" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(const int_array_1d *a, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + int_array_1d_print_internal(a, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create an array with 6 positions. + int_array_1d *a = int_array_1d_create(1, 6); + + printf("Empty array from the outside:\n"); + int_array_1d_print(a); + print_internal_with_cut_lines(a, "Empty array"); + + for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) { + // Store square of index. + int_array_1d_set_value(a, i*i, i); + } + // Print array after setting all values. + printf("Full array from the outside:\n"); + int_array_1d_print(a); + print_internal_with_cut_lines(a, "Full array from the inside"); + + int_array_1d_kill(a); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_list/Makefile b/OU4/src/int_list/Makefile new file mode 100644 index 0000000..75c4e20 --- /dev/null +++ b/OU4/src/int_list/Makefile @@ -0,0 +1,44 @@ +MWE = int_list_mwe1 int_list_mwe1i +TEST = int_list_test + +SRC = int_list.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Test programs. +test: $(TEST) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +int_list_mwe1: int_list_mwe1.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +int_list_mwe1i: int_list_mwe1i.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +int_list_test: int_list_test.c int_list.c + gcc -o $@ $(CFLAGS) $^ + +test_run: test + # Run the test + ./$(TEST) + +memtest_mwe: int_list_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test: int_list_test + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest: memtest_mwe memtest_test diff --git a/OU4/src/int_list/int_list.c b/OU4/src/int_list/int_list.c new file mode 100644 index 0000000..3d4b645 --- /dev/null +++ b/OU4/src/int_list/int_list.c @@ -0,0 +1,542 @@ +#include +#include +#include + +#include + +/* + * Implementation of a typed, undirected list of integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses linked + * 2-cells. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.01 2018-03-26: Bugfix: Corrected const declaration in remove. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v2.0 2024-05-10: Added print_internal. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list elements are implemented as two-cells with forward and + * backward links and place to store one integer. The list uses two + * border cells at the start and end of the list. + */ +typedef struct cell { + struct cell *next; + struct cell *prev; + int val; +} cell; + +struct list { + cell *head; + cell *tail; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void) +{ + // Allocate memory for the list head. + list *l = calloc(1, sizeof(list)); + + // Allocate memory for the border cells. + l->head = calloc(1, sizeof(cell)); + l->tail = calloc(1, sizeof(cell)); + + // Set consistent links between border elements. + l->head->next = l->tail; + l->tail->prev = l->head; + + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list * l) +{ + // List is empty if there are no cells between head and tail. + return (l->head->next == l->tail); +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list * l) +{ + // First position is position of first element. + return l->head->next; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list * l) +{ + // Last position is position *after* last element. + return l->tail; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); + } + return p->next; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p->prev; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->val; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list * l, int v, const list_pos p) +{ + // Allocate memory for a new cell. + list_pos e = malloc(sizeof(cell)); + + // Store the value. + e->val = v; + // Add links to/from the new cell. + e->next = p; + e->prev = p->prev; + p->prev = e; + e->prev->next = e; + + // Return the position of the new cell. + return e; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Remember return position. + list_pos next_pos = p->next; + // Link past this element. + p->prev->next = p->next; + p->next->prev = p->prev; + + // Free the memory allocated to the cell itself. + free(p); + // Return the position of the next element. + return next_pos; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. + * + * Returns: Nothing. + */ +void list_kill(list * l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + list_pos p = list_first(l); + + // Remove first element until list is empty. + while (!list_is_empty(l)) { + p = list_remove(l, p); + } + + // Free border elements and the list head. + free(l->head); + free(l->tail); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * + * Iterates over the list and print each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list * l) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + printf("[%d]\n", list_inspect(l, p)); + p = list_next(l, p); + } +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // Iterate over all positions in l. + list_pos q = list_first(l); + while (!list_pos_is_equal(l, q, list_end(l))) { + if (list_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = list_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY int_list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->head), PTR2ADDR(l->tail), PTR2ADDR(l)); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edges(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); + print_edge(indent_level, l, l->tail, "t", "tail", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const list_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%d|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), p->val, PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const list *l, const list_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + print_edge(indent_level, p, p->prev, "p", "prev", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + list_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/int_list/int_list_mwe1.c b/OU4/src/int_list/int_list_mwe1.c new file mode 100644 index 0000000..0847169 --- /dev/null +++ b/OU4/src/int_list/int_list_mwe1.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l); + + // Insert the value 2 at the second position in the list. + list_insert(l, 2, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_list/int_list_mwe1i.c b/OU4/src/int_list/int_list_mwe1i.c new file mode 100644 index 0000000..4b67328 --- /dev/null +++ b/OU4/src/int_list/int_list_mwe1i.c @@ -0,0 +1,97 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + printf("Empty list from the outside.\n"); + list_print(l); + print_internal_with_cut_lines(l, "Empty list showing the internals"); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List from the outside after inserting one value:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting one value showing the internals"); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List from the outside after inserting second value at the end:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals"); + + printf("List after removing first element:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after removing first element showing the internals"); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_list/int_list_test.c b/OU4/src/int_list/int_list_test.c new file mode 100644 index 0000000..2ffde69 --- /dev/null +++ b/OU4/src/int_list/int_list_test.c @@ -0,0 +1,1101 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create an empty list + list *l = list_empty(); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create an empty list + list *l = list_empty(); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create an empty list + list *l = list_empty(); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(); + // Insert an element at the only position in the empty list + list_insert(l, v, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(); + // Insert the first element + list_pos p = list_insert(l, v1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, v2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, 24, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int read_value = list_inspect(l, list_first(l)); + + if (!value_equal(read_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", read_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Insert new element at end + list_insert(l, v[1], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Insert new element at end + list_insert(l, v[2], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Insert new element at next(first()) + list_insert(l, v[1], list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (!value_equal(v[i], list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the end of the list + list_insert(l, n, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (!value_equal(i + 1, list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the beginning of the list + list_insert(l, n, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (!value_equal(n - i, list_inspect(l, p))) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_list datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU4/src/int_list_array/Makefile b/OU4/src/int_list_array/Makefile new file mode 100644 index 0000000..9cfd2b0 --- /dev/null +++ b/OU4/src/int_list_array/Makefile @@ -0,0 +1,44 @@ +MWE = int_list_array_mwe1 int_list_array_mwe1i +TEST = int_list_array_test + +SRC = int_list_array.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Test programs. +test: $(TEST) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +int_list_array_mwe1: int_list_array_mwe1.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +int_list_array_mwe1i: int_list_array_mwe1i.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +int_list_array_test: int_list_array_test.c int_list_array.c + gcc -o $@ $(CFLAGS) $^ + +test_run: test + # Run the test + ./$(TEST) + +memtest_mwe1: int_list_array_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test: int_list_array_test + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest: memtest_mwe1 memtest_test diff --git a/OU4/src/int_list_array/int_list_array.c b/OU4/src/int_list_array/int_list_array.c new file mode 100644 index 0000000..a54e1b5 --- /dev/null +++ b/OU4/src/int_list_array/int_list_array.c @@ -0,0 +1,515 @@ +#include +#include +#include // For bcopy +#include + +#include + +/* + * Implementation of a typed, undirected list of integers for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. The implementation uses a + * static array. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. Bugfix + * in list_remove. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.4 2024-05-10: Added print_internal. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The list is implemented as a static array. + */ +struct list { + int last_used_pos; + int *values; + int array_size; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +#define ARRAY_MAX_SIZE 100 + +/** + * list_empty() - Create an empty list. + * + * Returns: A pointer to the new list. + */ +list *list_empty(void) +{ + // Allocate memory for the list head. + list *l=malloc(sizeof(list)); + // Set the size + l->array_size = ARRAY_MAX_SIZE; + // Allocate memory for the elements. + l->values=calloc(l->array_size,sizeof(l->values[0])); + // Set last used position. + l->last_used_pos=-1; + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list * l) +{ + // List is empty if no elements are used. + return l->last_used_pos < 0; +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list * l) +{ + // First position is always 0. + return 0; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list * l) +{ + // Last position is position *after* last used element. + return l->last_used_pos + 1; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate " + "past end of list!"); + } + return p + 1; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p - 1; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The integer value stored in the element at postiion pos. + * NOTE: The return value is undefined for the last position. + */ +int list_inspect(const list * l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return l->values[p]; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Integer value to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list * l, int v, const list_pos p) +{ + // Move elements at position pos and later forward. + bcopy(l->values + p, l->values + p + 1, + sizeof(int) * (l->last_used_pos - p + 1)); + + // Set value. + l->values[p] = v; + + // Increment number of used elements. + l->last_used_pos++; + + // Return the position of the new value. + return p; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Move elements at position pos and later forward. + bcopy(l->values + p + 1, l->values + p, sizeof(int) * (l->last_used_pos - p)); + + // Decrement number of used elements. + l->last_used_pos--; + + // p now refers to the position after the removed element. + return p; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. + * + * Returns: Nothing. + */ +void list_kill(list * l) +{ + free(l->values); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * + * Iterates over the list and print each stored integer. + * + * Returns: Nothing. + */ +void list_print(const list * l) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + printf("[%d]\n",list_inspect(l, p)); + p = list_next(l, p); + } +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // The position is valid if it is between 0 and last_used_pos + 1, inclusive. + return p >= 0 && p <= l->last_used_pos+1; + +} + +// ===========INTERNAL FUNCTIONS USED BY int_list_array_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"last_used_pos\\n%d|array_size\\n%d|values\\n%04lx\"]\n", + PTR2ADDR(l), l->last_used_pos, l->array_size, PTR2ADDR(l->values)); +} + +/** + * print_values() - Print a node containing all positions in the array. + * @indent_level: Indentation level. + * @l: List to inspect. + * @max_values_to_print: Maximum number of values to output. + * + * Will print dot code to display each value in the array, up to + * max_values_to_print. If there are more values to print, adds an + * ellipsis (...) at the end. + * + * Returns: Nothing. + */ +void print_values(int indent_level, const list *l, int max_values_to_print) +{ + int values_to_print = l->array_size; + int truncated = false; + if (values_to_print > max_values_to_print) { + values_to_print = max_values_to_print; + truncated = true; + } + iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(l->values)); + for (int i=0; i < values_to_print; i++) { + printf("%02d\\n%d", i, l->values[i]); + if (i < values_to_print - 1) { + printf("|"); + } + } + if (truncated) { + // Add ellipsis + printf("|\\n...\\n"); + } + printf("\"]\n"); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edge(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->values, "v", "values", NULL); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, const char *desc, int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the List content + iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the values array. Limit the output to 10 elements. + print_values(il, l, 10); + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, l); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/int_list_array/int_list_array_mwe1.c b/OU4/src/int_list_array/int_list_array_mwe1.c new file mode 100644 index 0000000..d222f84 --- /dev/null +++ b/OU4/src/int_list_array/int_list_array_mwe1.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +int main(void) +{ + printf("%s, %s %s: Create typed integer list, implemented as an array.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l); + + // Insert the value 2 at the second position in the list. + list_insert(l, 2, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_list_array/int_list_array_mwe1i.c b/OU4/src/int_list_array/int_list_array_mwe1i.c new file mode 100644 index 0000000..4b67328 --- /dev/null +++ b/OU4/src/int_list_array/int_list_array_mwe1i.c @@ -0,0 +1,97 @@ +#include +#include + +#include + +/* + * Minimum working example for int_list.c. Create a list, insert two + * elements, print list, free list. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.11 2024-01-16: Fix include to be with brackets, not citation marks. + */ + +#define VERSION "v1.11" +#define VERSION_DATE "2024-01-16" + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(void) +{ + printf("%s, %s %s: Create typed integer list.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(); + + printf("Empty list from the outside.\n"); + list_print(l); + print_internal_with_cut_lines(l, "Empty list showing the internals"); + + // Insert the value 5 first in the list. + list_insert(l, 5, list_first(l)); + + printf("List from the outside after inserting one value:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting one value showing the internals"); + + // Insert the value 8 last in the list. + list_insert(l, 8, list_end(l)); + + printf("List from the outside after inserting second value at the end:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals"); + + printf("List after removing first element:\n"); + list_print(l); + print_internal_with_cut_lines(l, "List after removing first element showing the internals"); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_list_array/int_list_array_test.c b/OU4/src/int_list_array/int_list_array_test.c new file mode 100644 index 0000000..cf7fe4c --- /dev/null +++ b/OU4/src/int_list_array/int_list_array_test.c @@ -0,0 +1,1145 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2023-01-21: v1.0. First public version. This is effectively a copy of the int_list_test.c + * file. + * 2023-03-23: v1.1. Renamed list_pos_are_equal to list_pos_is_equal. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Run the code + list *l = list_empty(); + + // l should be non-NULL + if (l != NULL) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Run the code + list *l = list_empty(); + + // The list returned by empty() should be is_empty() + if (list_is_empty(l) == true) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Run the code + list *l = list_empty(); + + // first(l) should be == end(l) for an empty list + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(); + // Insert an element at the only position in the empty list + list_insert(l, v, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(); + // Insert the first element + list_pos p = list_insert(l, v1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, v2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (!list_is_empty(l)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (!list_pos_is_equal(l, list_first(l), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, 24, list_first(l)); + + // The returned position should be first in the modified list + if (list_pos_is_equal(l, p, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int read_value = list_inspect(l, list_first(l)); + + if (value_equal(read_value, val)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", read_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (!list_pos_is_equal(l, list_next(l, p), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (!list_pos_is_equal(l, list_prev(l, p), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (prev_is_inv_next(l, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (next_is_inv_prev(l, list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that next is inverse of prev on end() + if (list_is_empty(l)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (list_pos_is_equal(l, list_end(l), p)) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_first(l)); + + // Returned position should be == first(l) + if (list_pos_is_equal(l, p, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_insert(l, 30, list_end(l)); + + // Returned position should be == prev(end(l)) + if (list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (prev_is_inv_next(l, list_first(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (next_is_inv_prev(l, list_end(l))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Insert new element at end + list_insert(l, v[1], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Insert new element at end + list_insert(l, v[2], list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Insert new element at first + list_insert(l, v[0], list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Insert new element at next(first()) + list_insert(l, v[1], list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + if (value_equal(v[i], list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the end of the list + list_insert(l, n, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (value_equal(i + 1, list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", i + 1, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // insert the value n at the beginning of the list + list_insert(l, n, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + if (value_equal(n - i, list_inspect(l, p))) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + n - i, list_inspect(l, p)); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + // Insert one more element + list_insert(l, n, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(); + for (int i = 0; i < 5; i++) { + list_insert(l, i, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c == n) { + // Expected behaviour, do nothing + } else { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, expected %d\n", + c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_list_array datatype.\n", __FILE__, + VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU4/src/int_queue/.gitignore b/OU4/src/int_queue/.gitignore new file mode 100644 index 0000000..824be8c --- /dev/null +++ b/OU4/src/int_queue/.gitignore @@ -0,0 +1,2 @@ +int_queue_example_internal +int_queue_example diff --git a/OU4/src/int_queue/Makefile b/OU4/src/int_queue/Makefile new file mode 100644 index 0000000..44a2320 --- /dev/null +++ b/OU4/src/int_queue/Makefile @@ -0,0 +1,34 @@ +MWE = int_queue_mwe1 int_queue_mwe1i int_queue_example int_queue_example_internal + +SRC = int_queue.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_queue_mwe1: int_queue_mwe1.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_mwe1i: int_queue_mwe1i.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_example: int_queue_example.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +int_queue_example_internal: int_queue_example_internal.c int_queue.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: int_queue_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/int_queue/int_queue.c b/OU4/src/int_queue/int_queue.c new file mode 100644 index 0000000..51e521f --- /dev/null +++ b/OU4/src/int_queue/int_queue.c @@ -0,0 +1,310 @@ +#include +#include +#include + +#include + +/* + * Implementation of an integer queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The queue stores integers directly and does not use + * dynamic memory. Thus, the clean-up function queue_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic queue implementations. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +/** + * queue_empty() - Create an empty queue. + * + * Returns: A new, empty, queue. + */ +queue queue_empty(void) +{ + queue q; + q.first_free_pos = 0; + // Set all elements to zero. Not really needed, but otherwise + // the output from print_internal becomes unreadable. + for (int i = 0; i < sizeof(q.elements)/sizeof(q.elements[0]); i++) { + q.elements[i] = 0; + } + return q; +} + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue q) +{ + // The queue is empty if no positions are occupied with elements, + // i.e., the first free position is zero + return q.first_free_pos == 0; +} + +/** + * queue_enqueue() - Put a value at the end of a queue. + * @q: Queue to manipulate. + * @v: Value (integer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue queue_enqueue(queue q, int v) +{ + // Store value at first free position. + q.elements[q.first_free_pos] = v; + // Update first free position. + q.first_free_pos++; + + // Return the new queue. + return q; +} + +/** + * queue_dequeue() - Remove the element at the beginning of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue queue_dequeue(queue q) +{ + if (queue_is_empty(q)) { + fprintf(stderr, "queue_dequeue: Warning: dequeue on empty queue\n"); + } else { + // Shift all elements one step to the left + for (int i = 0; i < q.first_free_pos - 1; i++) { + q.elements[i] = q.elements[i + 1]; + } + // Update the position of the first free element + q.first_free_pos--; + } + return q; +} + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the front of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +int queue_front(const queue q) +{ + if (queue_is_empty(q)) { + fprintf(stderr, "queue_front: Warning: front on empty queue\n"); + } + return q.elements[0]; +} + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Does nothing since the queue does not use any dynamic + * memory. Included for symmetry with generic queue.h. + * + * Returns: Nothing. + */ +void queue_kill(queue q) +{ + // Do nothing. +} + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * + * Iterates over the queue and prints each integer. + * + * Returns: Nothing. + */ +void queue_print(const queue q) +{ + printf("{ "); + // Print elements from the top down. + for (int i = 0; i < q.first_free_pos; i++) { + printf("[%d]", q.elements[i]); + if (i < q.first_free_pos - 1) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i q [style=invis] " + "// Dummy line to place description above\n"); +} + +/** + * queue_print_internal() - Print the internal structure of the queue in dot format. + * @q: Queue to inspect. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the queue and outputs dot code that shows the + * internal structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph QUEUE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + // Output the head node + print_head_node(il, max_elems, q); + + // Output a dummy line to place the description at the top + print_desc_edge(il); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/int_queue/int_queue_example.c b/OU4/src/int_queue/int_queue_example.c new file mode 100644 index 0000000..1a3416e --- /dev/null +++ b/OU4/src/int_queue/int_queue_example.c @@ -0,0 +1,50 @@ +#include +#include + +#include + +/* + * A small example that uses an integer queue. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-16: First public version. + */ + +int main(void) +{ + // Create the queue. + queue q = queue_empty(); + + // The queue should be empty. Is it? + if (queue_is_empty(q)) { + printf("The newly created queue is empty.\n"); + } else { + printf("The newly created queue is NOT empty.\n"); + } + + for (int i = 11; i <= 13; i++) { + printf("Add the value %d at the end of the queue.\n", i); + // Put some values in the queue. + q = queue_enqueue(q, i); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + // Remove one element from the queue. + printf("Remove one element.\n"); + q = queue_dequeue(q); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_queue/int_queue_example_internal.c b/OU4/src/int_queue/int_queue_example_internal.c new file mode 100644 index 0000000..b4110fb --- /dev/null +++ b/OU4/src/int_queue/int_queue_example_internal.c @@ -0,0 +1,99 @@ +#include +#include + +#include + +// Size of internal string buffer +#define BUFLEN 400 + +/* + * A small example that uses an integer queue. + * + * Generates two kinds of output: + * 1) The 'outside' view showing the stored values only. + * 2) The 'inside' view showing the internal organization. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-24: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-24" + +// Print a message, then the queue and wait for the user to press return. +void print_and_wait(const queue q, const char *msg) +{ + printf("\nThe output from queue_print() shows the values stored in the queue\n"); + printf("\n%s\n", msg); + queue_print(q); + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_and_wait(queue q, const char *desc) +{ + // Print starting marker line. + printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n"); + printf("\n\n1) Copy the lines between the cut marks\n" + "2) Paste into the left half of the browser window.\n" + "3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n"); + printf("\n--- CUT HERE ---\n\n"); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, desc, 0, 10); + + // Print ending marker line + printf("\n--- END CUT HERE ---\n\n"); + + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +int main(void) +{ + // Message buffer + char buf[BUFLEN]; + + printf("%s, %s %s: Example use of a typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue q = queue_empty(); + + // Call print functions to show the content of the queue + print_and_wait(q, "The queue is empty"); + print_internal_and_wait(q, "The queue is empty"); + + for (int i = 11; i <= 13; i++) { + q = queue_enqueue(q, i); + snprintf(buf, BUFLEN, "The value %d was added to the queue", i); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + // Remove one element from the queue. + q = queue_dequeue(q); + snprintf(buf, BUFLEN, "The front element was removed from the queue"); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_queue/int_queue_mwe1.c b/OU4/src/int_queue/int_queue_mwe1.c new file mode 100644 index 0000000..8a5b416 --- /dev/null +++ b/OU4/src/int_queue/int_queue_mwe1.c @@ -0,0 +1,56 @@ +#include +#include + +#include + +/* + * Minimum working example for int_queue.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-10" + +int main(void) +{ + printf("%s, %s %s: Create typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue q = queue_empty(); + printf("--Empty QUEUE--\n"); + queue_print(q); + + + for (int i = 11; i <= 13; i++) { + // Put some values in the queue. + q = queue_enqueue(q, i); + printf("--QUEUE during insert--\n"); + queue_print(q); + } + + int v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", v); + + printf("--QUEUE after enqueue, before dequeueing--\n"); + queue_print(q); + + // Remove one element from the queue. + q = queue_dequeue(q); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_queue/int_queue_mwe1i.c b/OU4/src/int_queue/int_queue_mwe1i.c new file mode 100644 index 0000000..2a8c57b --- /dev/null +++ b/OU4/src/int_queue/int_queue_mwe1i.c @@ -0,0 +1,104 @@ +#include +#include + +#include + +/* + * Minimum working example for int_queue.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-10: Adapted from int_queue_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-10" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(queue s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(s, desc, 0, 10); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue q=queue_empty(); + + printf("Empty queue from the outside:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Empty queue showing the inside"); + + for (int i = 11; i <= 13; i++) { + // Put values in the queue. + q = queue_enqueue(q, i); + } + + printf("Queue for the outside after adding 3 elements:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after adding 3 elements"); + + // Remove one element from queue. + q = queue_dequeue(q); + + printf("Queue for the outside after removing 1 element:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after removing 1 element"); + + // Pop remaining elements + while (!queue_is_empty(q)) { + q = queue_dequeue(q); + } + + printf("Queue from the outside after removing all elements:\n"); + queue_print(q); + print_internal_with_cut_lines(q, "Inside of the queue after removing all elements"); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue_mwe1.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_stack/Makefile b/OU4/src/int_stack/Makefile new file mode 100644 index 0000000..9611338 --- /dev/null +++ b/OU4/src/int_stack/Makefile @@ -0,0 +1,28 @@ +MWE = int_stack_mwe1 int_stack_mwe1i + +SRC = int_stack.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +int_stack_mwe1: int_stack_mwe1.c int_stack.c + gcc -o $@ $(CFLAGS) $^ + +int_stack_mwe1i: int_stack_mwe1i.c int_stack.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: int_stack_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/int_stack/int_stack.c b/OU4/src/int_stack/int_stack.c new file mode 100644 index 0000000..0012c3d --- /dev/null +++ b/OU4/src/int_stack/int_stack.c @@ -0,0 +1,316 @@ +#include +#include +#include + +#include + +/* + * Implementation of an integer stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. The stack stores integers directly and does not use + * dynamic memory. Thus, the clean-up function stack_kill is strictly + * not necessary, but recommended to maintain symmetry with untyped, + * generic stack implementations. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se) + * Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2022-03-24: First public version. + * v1.1 2024-04-10: Added print_internal. + * v1.2 2024-05-10: Added/updated print_internal with improved encapsulation. + */ + +/** + * stack_empty() - Create an empty stack. + * + * Returns: An empty stack. + */ +stack stack_empty(void) +{ + stack s; + s.first_free_pos = 0; + // Set all elements to zero. Not really needed, but otherwise + // the output from print_internal becomes unreadable. + for (int i = 0; i < sizeof(s.elements)/sizeof(s.elements[0]); i++) { + s.elements[i] = 0; + } + return s; +} + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack s) +{ + // The stack is empty if no positions are occupied with elements, + // i.e., the first free position is zero + return s.first_free_pos == 0; +} + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (integer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_push(stack s, int v) +{ + // Store value at first free position. + s.elements[s.first_free_pos] = v; + // Update first free position. + s.first_free_pos++; + + // Return the new stack. + return s; +} + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack stack_pop(stack s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_pop: Warning: pop on empty stack\n"); + } else { + // We only have to decrease the first free position to + // indicate that the element that used to be on top of the + // stack is now free for use. + s.first_free_pos--; + } + return s; +} + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The integer at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +int stack_top(const stack s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_top: Warning: top on empty stack\n"); + } + return s.elements[s.first_free_pos - 1]; +} + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Does nothing since the stack does not use any dynamic + * memory. Included for symmetry with generic stack.h. + * + * Returns: Nothing. + */ +void stack_kill(stack s) +{ + // Do nothing. +} + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * + * Iterates over the stack and prints each integer. + * + * Returns: Nothing. + */ +void stack_print(const stack s) +{ + printf("{ "); + // Print elements from the top down. + for (int i = s.first_free_pos - 1; i >= 0; i--) { + printf("[%d]", s.elements[i]); + if (i>0) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i s [style=invis] " + "// Dummy line to place description above\n"); +} + +/** + * stack_print_internal() - Print the internal structure of the stack in dot format. + * @s: Stack to inspect. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost. + * @max_elems: Maximum number of elements to print. + * + * Iterates over the stack and outputs dot code that shows the + * internal structure of the stack. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph STACK_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + // Output the head node + print_head_node(il, max_elems, s); + + // Output a dummy line to place the description at the top + print_desc_edge(il); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/int_stack/int_stack_mwe1.c b/OU4/src/int_stack/int_stack_mwe1.c new file mode 100644 index 0000000..cd5039d --- /dev/null +++ b/OU4/src/int_stack/int_stack_mwe1.c @@ -0,0 +1,48 @@ +#include +#include + +#include + +/* + * Minimum working example for int_stack.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2022-03-24: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2023-01-14" + +int main(void) +{ + printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. + stack s=stack_empty(); + + for (int i=1; i<=3; i++) { + // Push value on stack. + s = stack_push(s, i); + } + + printf("--STACK before popping--\n"); + stack_print(s); + + // Pop element from stack. + s=stack_pop(s); + + printf("--STACK after popping--\n"); + stack_print(s); + + // Kill stack. Strictly not necessary for int_stack since it + // does not use any dynamic memory. Included for symmetry with + // stack.c. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/int_stack/int_stack_mwe1i.c b/OU4/src/int_stack/int_stack_mwe1i.c new file mode 100644 index 0000000..dcd4c97 --- /dev/null +++ b/OU4/src/int_stack/int_stack_mwe1i.c @@ -0,0 +1,103 @@ +#include +#include + +#include + +/* + * Minimum working example for int_stack.c. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-10: Adapted from int_stack_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-10" + +// Print cut lines before and after a call array_print_internal. +void print_internal_with_cut_lines(stack s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, desc, 0, 10); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack s=stack_empty(); + + printf("Empty stack from the outside:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Empty stack showing the inside"); + + for (int i=1; i<=3; i++) { + // Push value on stack. + s = stack_push(s, 10 + i); + } + + printf("Stack for the outside after pushing 3 elements:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after pushing 3 elements"); + + // Pop one element from stack. + s = stack_pop(s); + + printf("Stack for the outside after popping 1 element:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after popping 1 element"); + + // Pop remaining elements + while (!stack_is_empty(s)) { + s = stack_pop(s); + } + + printf("Stack from the outside after popping all elements:\n"); + stack_print(s); + print_internal_with_cut_lines(s, "Inside of the stack after popping all elements"); + + // Kill stack. Strictly not necessary for int_stack since it + // does not use any dynamic memory. Included for symmetry with + // stack_mwe1.c. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/list/Makefile b/OU4/src/list/Makefile new file mode 100644 index 0000000..ea97d56 --- /dev/null +++ b/OU4/src/list/Makefile @@ -0,0 +1,68 @@ +MWE = list_mwe1 list_mwe2 list_mwe1i list_mwe2i +TEST = list_test1 list_test2 + +SRC = list.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe test + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +test: list_test1 list_test2 + +# Clean up +clean: + -rm -f $(MWE) $(TEST) $(OBJ) + +list_mwe1: list_mwe1.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe2: list_mwe2.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe1i: list_mwe1i.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_mwe2i: list_mwe2i.c list.c + gcc -o $@ $(CFLAGS) $^ + +memtest_mwe1: list_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe2: list_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe1i: list_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_mwe2i: list_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< + +list_test1: list_test1.c list.c + gcc -o $@ $(CFLAGS) $^ + +list_test2: list_test2.c list.c + gcc -o $@ $(CFLAGS) $^ + +test_run1: list_test1 + # Run the test + ./$< + +test_run2: list_test2 + # Run the test + ./$< + +memtest_test1: list_test1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest_test2: list_test2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtests: memtest_mwe1 memtest_mwe2 memtest_mwe1i memtest_mwe2i memtest_test1 memtest_test2 diff --git a/OU4/src/list/README.md b/OU4/src/list/README.md new file mode 100644 index 0000000..3d203fd --- /dev/null +++ b/OU4/src/list/README.md @@ -0,0 +1,111 @@ +# Dubbellänkad lista +En två-cellsimplementation av ADT:n _Lista_. + +## Minneshantering + +Följande kodsnutt kommer skapa en lista med ett element som har värdet 5. I +detta exempel så allokeras inget minne dynamiskt, därför behöver vi inte heller +ta hand om något minne efter vi använt klart listan. + +```c +int val = 5; +list *l = list_empty(NULL); +list_insert(l, &val, list_first(l)); +``` + +Vi kan skriva om ovan exempel så att minne allokeras på heapen, men då måste vi +manuellt komma ihåg att avallokera detta minne efter användning. + +```c +int *val = malloc(sizeof(int)); +*val = 5; + +list *l = list_empty(NULL); +list_insert(l, val, list_first(l)); +... +list_kill(l); +free(val); +``` + +Om vi vill allokera minne dynamiskt, men vill att listan själva ska hantera +avallokering av minne (e.g. vid `list_remove`) så behöver vi ange en +minneshanteringsfunktion: +```c +int *val = malloc(sizeof(int)); +*val = 5; + +list *l = list_empty(free); +list_insert(l, val, list_first(l)); +... +list_kill(l); +``` + +I exemplet ovan kommer `list_kill(l)` att anropa `free` för varje element i +listan, och därmed sköta minneshanteringen åt oss. + +### Egen free-funktion + +Det är också möjligt att definiera en egen funktion som ska sköta +avallokeringen. Till exempel kanske vi vill skriva ut varje värde som +avallokeras i debugging-syfte. Då kan vi skriva en enkel wrapper-funktion för +`free` på följande sätt: + +```c +static void free_and_print(void *data) +{ + printf("Free'ing [%d]\n", *(int*)data); + free(data); +} +``` + +Denna funktion skulle då kunna användas på följande sätt: + +```c +list *l = list_empty(free_and_print); +int *val = malloc(sizeof(int)); +*val = 5; +list_insert(l, val, list_first(l)); +list_kill(l); +``` + +Denna kodsnutt skulle då skriva ut `Free'ing [5]`. + + +## Utskrift + +För att skriva ut listan, t.ex. vid debugging eller för att visa innehållet för +en användare så kan vi definiera en egen funktion för att presentera tillståndet +hos datatypen. Det enklaste fallet är om vi enbart vill skriva ut, e.g., en +lista med heltal där varje värde hamnar på en egen rad: + +```c +static void print_ints(const void *data) +{ + printf("%d\n", *(int*)data); +} +``` + +Notera att inparametern är av typen `const`. `const` indikerar att pekaren bara +får läsas, ej manipuleras. Vi vill inte att en print-funktion ska kunna ändra på +listans innehåll. + +`print_ints` kan nu användas på följande sätt: + +```c +list *l = list_empty(free); +int *val = malloc(sizeof(int)); +*val = 5; +list_insert(l, val, list_first(l)); + +list_print(l, print_ints); +``` + +Detta exempel skulle skriva ut `5` på en egen rad. + + +# Minimal working example + +Mycket av det som behandlats ovan sammanfattas i följande minimal working +example: + +Se [list_mwe1.c](list_mwe1.c) och [list_mwe2.c](list_mwe2.c). diff --git a/OU4/src/list/list.c b/OU4/src/list/list.c new file mode 100644 index 0000000..a09391a --- /dev/null +++ b/OU4/src/list/list.c @@ -0,0 +1,620 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic, undirected list for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * Lars Karlsson (larsk@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. + * v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal. + * v1.3 2023-02-25: Renamed list header fields head and tail. + * v1.4 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.0 2024-03-14: Added list_print_internal to output dot code for visualization. + * v2.1 2024-05-10: Added/updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +/* + * The list elements are implemented as two-cells with forward and + * backward links and a void * for the value. The list uses two border + * cells at the start and end of the list. + */ +typedef struct cell { + struct cell *next; + struct cell *prev; + void *val; +} cell; + +struct list { + cell *head; + cell *tail; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * list_empty() - Create an empty list. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new list. + */ +list *list_empty(kill_function kill_func) +{ + // Allocate memory for the list head. + list *l = calloc(1, sizeof(list)); + + // Allocate memory for the border cells. + l->head = calloc(1, sizeof(cell)); + l->tail = calloc(1, sizeof(cell)); + + // Set consistent links between border elements. + l->head->next = l->tail; + l->tail->prev = l->head; + + // Store the kill function. + l->kill_func = kill_func; + + return l; +} + +/** + * list_is_empty() - Check if a list is empty. + * @l: List to check. + * + * Returns: True if the list is empty, otherwise false. + */ +bool list_is_empty(const list *l) +{ + // List is empty if there are no cells between head and tail. + return (l->head->next == l->tail); +} + +/** + * list_first() - Return the first position of a list, i.e. the + * position of the first element in the list. + * @l: List to inspect. + * + * Returns: The first position in the given list. + */ +list_pos list_first(const list *l) +{ + // First position is position of first element. + return l->head->next; +} + +/** + * list_end() - Return the last position of a list, i.e. the position + * after the last element in the list. + * @l: List to inspect. + * + * Returns: The last position in the given list. + */ +list_pos list_end(const list *l) +{ + // Last position is position *after* last element. + return l->tail; +} + +/** + * list_next() - Return the next position in a list. + * @l: List to inspect. + * @p: Any valid position except the last in the list. + * + * Returns: The position in the list after the given position. + * NOTE: The return value is undefined for the last position. + */ +list_pos list_next(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!"); + } + return p->next; +} + +/** + * list_prev() - Return the previous position in a list. + * @l: List to inspect. + * @p: Any valid position except the first in the list. + * + * Returns: The position in the list before the given position. + * NOTE: The return value is undefined for the first position. + */ +list_pos list_prev(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_first(l))) { + // This should really throw an error. + fprintf(stderr,"list_prev: Warning: Trying to navigate " + "past beginning of list!\n"); + } + return p->prev; +} + +/** + * list_inspect() - Return the value of the element at a given + * position in a list. + * @l: List to inspect. + * @p: Any valid position in the list, except the last. + * + * Returns: The value at the given position as a void pointer. + * NOTE: The return value is undefined for the last position. + */ +void *list_inspect(const list *l, const list_pos p) +{ + if (list_pos_is_equal(l, p, list_end(l))) { + // This should really throw an error. + fprintf(stderr,"list_inspect: Warning: Trying to inspect " + "position at end of list!\n"); + } + return p->val; +} + +/** + * list_insert() - Insert a new element with a given value into a list. + * @l: List to manipulate. + * @v: Value (pointer) to be inserted into the list. + * @p: Position in the list before which the value should be inserted. + * + * Creates a new element and inserts it into the list before p. + * Stores data in the new element. + * + * Returns: The position of the newly created element. + */ +list_pos list_insert(list *l, void *v, const list_pos p) +{ + // Allocate memory for a new cell. + list_pos e = malloc(sizeof(cell)); + + // Store the value. + e->val = v; + // Add links to/from the new cell. + e->next = p; + e->prev = p->prev; + p->prev = e; + e->prev->next = e; + + // Return the position of the new cell. + return e; +} + +/** + * list_remove() - Remove an element from a list. + * @l: List to manipulate. + * @p: Position in the list of the element to remove. + * + * Removes the element at position p from the list. If a kill_func + * was registered at list creation, calls it to deallocate the memory + * held by the element value. + * + * Returns: The position after the removed element. + */ +list_pos list_remove(list *l, const list_pos p) +{ + // Remember return position. + list_pos next_pos = p->next; + // Link past this element. + p->prev->next = p->next; + p->next->prev = p->prev; + + // Call kill_func if registered. + if (l->kill_func != NULL) { + // Return any user-allocated memory for the value. + l->kill_func(p->val); + } + // Return the memory allocated to the cell itself. + free(p); + // Return the position of the next element. + return next_pos; +} + +/** + * list_kill() - Destroy a given list. + * @l: List to destroy. + * + * Returns all dynamic memory used by the list and its elements. If a + * kill_func was registered at list creation, also calls it for each + * element to return any user-allocated memory occupied by the element + * values. + * + * Returns: Nothing. + */ +void list_kill(list *l) +{ + // Use public functions to traverse the list. + + // Start with the first element (will be defined even for an + // empty list). + list_pos p = list_first(l); + + // Remove first element until list is empty. + while (!list_is_empty(l)) { + p = list_remove(l, p); + } + + // Free border elements and the list head. + free(l->head); + free(l->tail); + free(l); +} + +/** + * list_print() - Iterate over the list element and print their values. + * @l: List to inspect. + * @print_func: Function called for each element. + * + * Iterates over the list and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void list_print(const list *l, inspect_callback print_func) +{ + // Start at the beginning of the list. + list_pos p = list_first(l); + + printf("( "); + while (!list_pos_is_equal(l, p, list_end(l))) { + // Call print_func with the element value at the + // current position. + print_func(list_inspect(l, p)); + // Advance to next position. + p = list_next(l, p); + // Print separator unless at end. + if (!list_pos_is_equal(l, p, list_end(l))) { + printf(", "); + } + } + printf(" )\n"); +} + +/** + * list_pos_is_equal() - Return true if two positions in a list are equal. + * @l: List to inspect. + * @p1: First position to compare. + * @p2: Second position to compare. + * + * Returns: True if p1 and p2 refer to the same position in l, otherwise False. + * NOTE: The result is defined only if p1 and p2 are valid positions in l. + */ +bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2) +{ + // Since we don't need to check whether p1 or p2 are valid, we + // only need to compare them directly. + return p1 == p2; +} + +/** + * list_pos_is_valid() - Return true for a valid position in a list + * @l: List to inspect. + * @p: Any position. + * + * Returns: True if p is a valid position in the list, otherwise false. + */ +bool list_pos_is_valid(const list *l, const list_pos p) +{ + // Iterate over all positions in l. + list_pos q = list_first(l); + while (!list_pos_is_equal(l, q, list_end(l))) { + if (list_pos_is_equal(l, p, q)) { + // We found the position in the list. + return true; + } + // Advance to the next valid position, + q = list_next(l, q); + } + // p was not among valid positions in l. + return false; +} + +// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the list head struct. + * @indent_level: Indentation level. + * @l: List to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const list *l) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"kill\\n%04lx|head\\n%04lx|tail\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l->tail), + PTR2ADDR(l)); +} + +// Print edges from the list head to the head and tail cells. +static void print_head_edges(int indent_level, const list *l) +{ + print_edge(indent_level, l, l->head, "h", "head", NULL); + print_edge(indent_level, l, l->tail, "t", "tail", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const list_pos p) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"val\\n%04lx|next\\n%04lx|

prev\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the list owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const list *l, const list_pos p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + print_edge(indent_level, p, p->prev, "p", "prev", NULL); + + // Ignore val ptr for head and tail nodes. + if (p == l->head || p == l->tail) { + return; + } + if (l->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * list_print_internal() - Print the lists internal structure in dot format. + * @l: List to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the list, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and outputs dot code that shows the internal + * structure of the list. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void list_print_internal(const list *l, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph LIST_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l)); + iprintf(il, "l -> m%04lx\n", PTR2ADDR(l)); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_list_%d { label=\"List\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, l); + + // Output the element nodes + list_pos p = l->head; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = l->head; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edges(il, l); + + // Output the edges from each element + p = l->head; + while (p != NULL) { + print_elem_edges(il, l, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/list/list_mwe1.c b/OU4/src/list/list_mwe1.c new file mode 100644 index 0000000..8d7d20d --- /dev/null +++ b/OU4/src/list/list_mwe1.c @@ -0,0 +1,103 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +static void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer list without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(NULL); + + // Create a dynamic integer with value 5. + int *val = int_create(5); + + // Insert the value first in the list. + list_insert(l, val, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l, print_int); + + // Allocate space for another integer. + val = int_create(8); + // Insert the value last in the list. + list_insert(l, val, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l, print_int); + + // Allocate space for a third integer. + val = int_create(2); + // Insert the value at the second position in the list. + list_insert(l, val, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l, print_int); + + // Remove first element. + list_pos p=list_first(l); + int *v=list_inspect(l, p); + // Remove element from list. + list_remove(l, p); + // Free element content. + int_kill(v); + + printf("List after removing first element:\n"); + list_print(l, print_int); + + // Empty the list. + while (!list_is_empty(l)) { + list_pos p=list_first(l); + int *v=list_inspect(l,p); + int_kill(v); + list_remove(l,p); + } + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/list/list_mwe1i.c b/OU4/src/list/list_mwe1i.c new file mode 100644 index 0000000..e350614 --- /dev/null +++ b/OU4/src/list/list_mwe1i.c @@ -0,0 +1,131 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "borrows" the payload memory, i.e., the user of the list is + * responsible for deallocating the payload memory. See list_mwe2i.c + * for a version where the list takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer list without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the list. + list *l = list_empty(NULL); + + printf("Empty list from the outside:\n"); + list_print(l, print_int); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + // Insert the value 5 first in the list. + list_insert(l, int_create(5), list_first(l)); + // Insert the value 8 last in the list. + list_insert(l, int_create(8), list_end(l)); + // Insert the value 2 at the second position in the list. + list_insert(l, int_create(2), list_next(l, list_first(l))); + + printf("List from the outside after inserting 3 values:\n"); + list_print(l, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the List after inserting 3 values.\n" + "Red lines are used for the list payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the list, i.e., the payload\n" + "memory will NOT be deallocated by the list.\n\n" + "See list_mwe2i for a list example\nthat owns the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Empty the list. + while (!list_is_empty(l)) { + list_pos p=list_first(l); + int *v=list_inspect(l,p); + int_kill(v); + list_remove(l,p); + } + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/list/list_mwe2.c b/OU4/src/list/list_mwe2.c new file mode 100644 index 0000000..0820443 --- /dev/null +++ b/OU4/src/list/list_mwe2.c @@ -0,0 +1,92 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.21 2024-01-16: Fix include to be with brackets, not citation marks. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +static void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer list with custom kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the list. + list *l = list_empty(int_kill); + + // Create a dynamic integer with value 5. + int *val = int_create(5); + + // Insert the value first in the list. + list_insert(l, val, list_first(l)); + + printf("List after inserting one value:\n"); + list_print(l, print_int); + + // Allocate space for another integer. + val = int_create(8); + // Insert the value last in the list. + list_insert(l, val, list_end(l)); + + printf("List after inserting second value at the end:\n"); + list_print(l, print_int); + + // Allocate space for a third integer. + val = int_create(2); + // Insert the value at the second position in the list. + list_insert(l, val, list_next(l, list_first(l))); + + printf("List after inserting a third value in the middle:\n"); + list_print(l, print_int); + + // Remove first element. + list_remove(l, list_first(l)); + + printf("List after removing first element:\n"); + list_print(l, print_int); + + // Done, kill the list. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/list/list_mwe2i.c b/OU4/src/list/list_mwe2i.c new file mode 100644 index 0000000..8866d32 --- /dev/null +++ b/OU4/src/list/list_mwe2i.c @@ -0,0 +1,125 @@ +#include +#include + +#include + +/* + * Minimum working example for list.c that shows how the internal + * structure of a list can be visualized. In this version, the list + * "owns" the payload memory, i.e., the list takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See list_mwe1i.c for a version + * where the list does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const list *l, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + list_print_internal(l, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer list with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the list. + list *l = list_empty(int_kill); + + printf("Empty list from the outside:\n"); + list_print(l, print_int); + print_internal_with_cut_lines(l, "Empty list showing the internal structure"); + + // Insert the value 5 first in the list. + list_insert(l, int_create(5), list_first(l)); + // Insert the value 8 last in the list. + list_insert(l, int_create(8), list_end(l)); + // Insert the value 2 at the second position in the list. + list_insert(l, int_create(2), list_next(l, list_first(l))); + + printf("List from the outside after inserting 3 values:\n"); + list_print(l, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the List after inserting 3 values.\n" + "Red lines are used for the list payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the list, i.e., the payload\n" + "memory WILL be deallocated by the list.\n\n" + "See list_mwe1i for a list example\nthat borrows the payload memory."; + print_internal_with_cut_lines(l, long_desc); + + // Kill the list, including the payload. + list_kill(l); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/list/list_test1.c b/OU4/src/list/list_test1.c new file mode 100644 index 0000000..ea4e0dc --- /dev/null +++ b/OU4/src/list/list_test1.c @@ -0,0 +1,1190 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Test program for the list implementation in list.c. This version + * uses a free-handler, i.e., the list will be responsible for + * deallocating any elements we allocate and put in it. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * v1.0 2023-01-21: First public version. + * v1.1 2023-03-21: Removed empty if statements in test code. + * v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create the list with a free-handler. + list *l = list_empty(int_kill); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(int_kill); + // Make a dynamic copy of v. + int *q = int_create(v); + // Insert an element at the only position in the empty list + list_insert(l, q, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(int_kill); + // Make dynamic copies of v1 and v2 + int *q1 = int_create(v1); + int *q2 = int_create(v2); + // Insert the first element + list_pos p = list_insert(l, q1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, q2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(int_kill); + // Create a dynamic integer + int *q = int_create(24); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, q, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int *stored_ptr = list_inspect(l, list_first(l)); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of element value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Make dynamic copy of element to insert + int *q1 = int_create(v[1]); + + // Insert new element at end + list_insert(l, q1, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Make dynamic copy of element to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Make dynamic copy of value to insert + int *q2 = int_create(v[2]); + + // Insert new element at end + list_insert(l, q2, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Make dynamic copy of value to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Make dynamic copy of value to insert + int *q1 = int_create(v[1]); + + // Insert new element at next(first()) + list_insert(l, q1, list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(int_kill); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the end of the list + list_insert(l, q, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(i + 1, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(int_kill); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the beginning of the list + list_insert(l, q, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(n - i, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(int_kill); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(int_kill); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(int_kill); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(int_kill); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the generic list.\n" + "Uses the standard free-handler to handle deallocation of dynamic memory.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Uses code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU4/src/list/list_test2.c b/OU4/src/list/list_test2.c new file mode 100644 index 0000000..7bfcd8e --- /dev/null +++ b/OU4/src/list/list_test2.c @@ -0,0 +1,1308 @@ +#include +#include +#include // for EXIT_FAILURE +#include + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Test program for the list implementation in list.c. This version + * does not use a free-handler, i.e., the list will not be responsible + * for deallocating any allocated elements stored in it. Instead, the + * responsibility remains with the user of the list. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * v1.0 2023-01-21: First public version. + * v1.1 2023-03-21: Removed empty if statements in test code. + * v1.2 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal. + * v1.3 2024-03-13: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-13" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/** + * int_create() - make a dynamic copy of an integer + * @i: Integer to copy + * + * Returns: A pointer to a dynamic copy of i + */ +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +/* + * empty_returns_non_null() - Test that the list_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // l should be non-NULL + if (l == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_is_empty() - Test that the list_empty() list is empty. + * Precondition: list_empty() returns non-null. + */ +void empty_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_is_empty()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // The list returned by empty() should be is_empty() + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty(empty()) == false, expected true\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * empty_first_end() - Test that first() == end() on an empty list. + * Precondition: list_is_empty(l) == false. + */ +void empty_first_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_first_end()..."); + + // Create the list without a free-handler. + list *l = list_empty(NULL); + + // first(l) should be == end(l) for an empty list + if (!(list_pos_is_equal(l, list_first(l), list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: expected first(l) == end(l), they are not\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * kill_all_element_values() - kill all element values stored in the list + * @l - List to operate on + * + * Returns: A pointer to the modified list + * + * The function traverses the list and calls int_kill for the value (void *) + * stored in each element. WARNING: This should only be executed + * immediately before the list is killed since all element values are + * UNDEFINED after the call. + */ +list *kill_all_element_values(list *l) +{ + // Iterate over all elements + list_pos p=list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + // Read the stored pointer value + void *q = list_inspect(l, p); + // Free its memory + int_kill(q); + // Advance to the next element + p = list_next(l, p); + } + return l; +} + +/** + * create_one_element_list() - Create a list with one element + * @v: Value to insert + * + * Preconditions: list_empty() and list_first() works. + * + * Returns: A list with one element with the value v. + * + * The list is created by inserting an element at the first position + * of an empty list. + */ +list *create_one_element_list(int v) +{ + // Create the list + list *l = list_empty(NULL); + // Make a dynamic copy of v. + int *q = int_create(v); + // Insert an element at the only position in the empty list + list_insert(l, q, list_first(l)); + + return l; +} + +/** + * create_two_element_list() - Create a list with two elements + * @v1: Value to insert first + * @v2: Value to insert at the end + * + * Preconditions: list_empty(), list_first(), list_next(), list_insert() works. + * + * Returns: A list with two elements with the values v1 and v2 in that order. + * + * The the element with value v2 is inserted after the element with value v1. + */ +list *create_two_element_list(int v1, int v2) +{ + // Create the list + list *l = list_empty(NULL); + // Make dynamic copies of v1 and v2 + int *q1 = int_create(v1); + int *q2 = int_create(v2); + // Insert the first element + list_pos p = list_insert(l, q1, list_first(l)); + // Insert the second element *after* the first + list_insert(l, q2, list_next(l, p)); + + return l; +} + +/* + * one_element_list_is_nonempty() - Test that a list with one element is not empty. + * Precondition: list_empty() returns non-null. + * first(l) == end(l) + */ +void one_element_list_is_nonempty(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_is_nonempty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should be non-empty + if (list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: is_empty after insert == true, expected false\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_has_first_neq_end() - Test that a list with one element has first() != end(). + * Precondition: list_empty(), list_first() works + */ +void one_element_list_has_first_neq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_has_first_neq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // One-element list should have first() != end() + if (list_pos_is_equal(l, list_first(l), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: one-element list has first() == end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_first_returns_correct_pos() - Test that insert(first()) returns the correct pos. + * Precondition: list_empty(), list_first() works + */ +void insert_first_returns_correct_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_first_returns_correct_pos()..."); + + // Create the list + list *l = list_empty(NULL); + // Create a dynamic integer + int *q = int_create(24); + // Insert an element with an arbitrary value + list_pos p = list_insert(l, q, list_first(l)); + + // The returned position should be first in the modified list + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: position returned by insert() != first\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * inserted_element_has_correct_value() - Test that the inserted value can be recovered. + * Precondition: list_empty(), list_first() works, list_insert() returns correct pos. + */ +void inserted_element_has_correct_value(void) +{ + // Print a starting message + fprintf(stderr,"Starting inserted_element_has_correct_value()..."); + + // Decide an arbitrary value to insert + int val = 24; + // Create a one-element list with the value + list *l = create_one_element_list(24); + + // The value at the returned position should be the one we inserted + int *stored_ptr = list_inspect(l, list_first(l)); + int stored_value = *stored_ptr; + + if (!value_equal(stored_value, val)) { + // Fail with error message + fprintf(stderr, "FAIL: inspect returned %d, expected %d\n", stored_value, val); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * next_does_something() - Test that next returns another position than its argument. + * Precondition: list_empty(), list_first() works, + */ +void next_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting next_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_first(l); + + // Next(p) should be != p + if (list_pos_is_equal(l, list_next(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_eq_end() - Test that next on first() in a one-element list returns end(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_eq_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Next(First()) should be == End(l) + if (!list_pos_is_equal(l, list_next(l, list_first(l)), list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected next(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * prev_does_something() - Test that prev returns another position than its argument. + * Precondition: list_empty(), list_first(), list_end() works + */ +void prev_does_something(void) +{ + // Print a starting message + fprintf(stderr,"Starting prev_does_something()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + list_pos p = list_end(l); + + // Prev(p) should be != p + if (list_pos_is_equal(l, list_prev(l, p), p)) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) != p\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_prev_end_eq_first() - Test that prev on + * end() in a one-element list returns first(). + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_end_eq_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_eq_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Prev(End()) should be == First(l) + if (!list_pos_is_equal(l, list_prev(l, list_end(l)), list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: expected prev(p) == end(l)\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * prev_is_inv_next() - Check if prev is inverse of next + * @l: A list with a least one element + * @p: A position, not equal to end + * + * Returns: True if Prev(l, Next(l, p)) == p + */ +bool prev_is_inv_next(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_prev(l, list_next(l, p)), p); +} + +/** + * next_is_inv_prev() - Check if next is inverse of prev + * @l: A list with a least one element + * @p: A position, not equal to first + * + * Returns: True if Next(l, Prev(l, p)) == p + */ +bool next_is_inv_prev(const list *l, const list_pos p) +{ + return list_pos_is_equal(l, list_next(l, list_prev(l, p)), p); +} + +/* + * one_element_list_prev_is_inv_next() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_prev_is_inv_next(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_prev_is_inv_next()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that prev is inverse of next on first() + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_next_is_inv_prev() - Test that prev is inverse to next in a + * one-element list. + * Precondition: list_empty(), list_first(), list_end() works + */ +void one_element_list_next_is_inv_prev(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_next_is_inv_prev()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Check that next is inverse of prev on end() + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end() for one-element list\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_is_empty() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_is_empty(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_is_empty()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove only element + list_remove(l, list_first(l)); + + // Check that list is empty + if (!list_is_empty(l)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) list is non-empty\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_remove_returns_end() - Test that insert followed by remove gives empty list. + * Precondition: list_empty(), list_is_empty(), list_first(), list_end(), list_insert() works + */ +void insert_remove_returns_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_remove_returns_end()..."); + + // Create a one-element list storing an arbitrary value + list *l = create_one_element_list(24); + + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove only element + list_pos p = list_remove(l, list_first(l)); + + // Check that remove(first()) on one-element list returns end() + if (!list_pos_is_equal(l, list_end(l), p)) { + // Fail with error message + fprintf(stderr, "FAIL: remove(insert()) did not return end()\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_first_pos() - Check return value of insert(first()) on + * one-element list + * + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_first_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_first_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_first(l)); + + // Returned position should be == first(l) + if (!list_pos_is_equal(l, p, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(first()) on one-element list did not " + "return correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_check_insert_end_pos() - Check return value of insert(end()) on + * one-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), works + */ +void one_element_list_check_insert_end_pos(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_check_insert_end_pos()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_one_element_list(24); + + // Make dynamic copy of element value to insert + int *q = int_create(30); + + list_pos p = list_insert(l, q, list_end(l)); + + // Returned position should be == prev(end(l)) + if (!list_pos_is_equal(l, p, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: insert(end()) on one-element list did not return " + "correct position\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_check_links() - Test next(first()) and prev(end()) are equal on + * two-element list + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_check_links(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_check_links()..."); + + // Create a two-element lists storing an arbitrary value + list *l = create_two_element_list(24, 30); + + // Check prev(next()) on each valid position... + + // ...start with first + if (!prev_is_inv_next(l, list_first(l))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on first()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_next(l, list_first(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: prev(next()) failed on next(first())\n"); + exit(EXIT_FAILURE); + } + + // Check next(prev()) on each valid position... + + // ...start with end + if (!next_is_inv_prev(l, list_end(l))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on end()\n"); + exit(EXIT_FAILURE); + } + + // ...then middle + if (!prev_is_inv_next(l, list_prev(l, list_end(l)))) { + // Fail with error message + fprintf(stderr, "FAIL: next(prev()) failed on prev(end())\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_end() - Insert a value at end() of a one-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_end()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 11 + list *l = create_one_element_list(v[0]); + + // Make dynamic copy of element to insert + int *q1 = int_create(v[1]); + + // Insert new element at end + list_insert(l, q1, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * one_element_list_insert_first() - Insert a value at first() of a one-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void one_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting one_element_list_insert_first()..."); + + // Numbers in the expected order + int v[2] = { 11, 12 }; + + // Create a one-element lists with 12 + list *l = create_one_element_list(v[1]); + + // Make dynamic copy of element to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_end() - Insert a value at end() of a two-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_end()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[1]); + + // Make dynamic copy of value to insert + int *q2 = int_create(v[2]); + + // Insert new element at end + list_insert(l, q2, list_end(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_first() - Insert a value at first() of a two-element list and + * check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_first()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[1],v[2]); + + // Make dynamic copy of value to insert + int *q0 = int_create(v[0]); + + // Insert new element at first + list_insert(l, q0, list_first(l)); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(first())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * two_element_list_insert_middle() - Insert a value in the middle of a two-element list + * and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void two_element_list_insert_middle(void) +{ + // Print a starting message + fprintf(stderr,"Starting two_element_list_insert_middle()..."); + + // Numbers in the expected order + int v[3] = { 11, 12, 13 }; + + // Create a two-element lists with 11 + list *l = create_two_element_list(v[0],v[2]); + + // Make dynamic copy of value to insert + int *q1 = int_create(v[1]); + + // Insert new element at next(first()) + list_insert(l, q1, list_next(l, list_first(l))); + + // Check that we got the expected element order + list_pos p = list_first(l); + + for (int i = 0; i < LEN(v); i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(v[i], stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(middle())\n", + v[i], stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_end() - Insert a value at end() of a n-element list and check the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_end(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_end()..."); + + // Start with an empty list + list *l = list_empty(NULL); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the end of the list + list_insert(l, q, list_end(l)); + + // Check that the element values are in the expected order, 1, ..., n + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(i + 1, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after insert(end())\n", + i + 1, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * n_element_list_insert_first() - Insert a value at first() of a n-element list and check + * the order + * Precondition: list_first(), list_end(), list_next(), list_prev(), list_insert() works + */ +void n_element_list_insert_first(void) +{ + // Print a starting message + fprintf(stderr,"Starting n_element_list_insert_first()..."); + + // Start with an empty list + list *l = list_empty(NULL); + + // n is the number of elements in the list + for (int n = 1; n <= 5; n++) { + // Make dynamic copy of value to insert + int *q = int_create(n); + + // insert the value n at the beginning of the list + list_insert(l, q, list_first(l)); + + // Check that the element values are in the expected order n, n-1, ..., 1 + list_pos p = list_first(l); + + for (int i = 0; i < n; i++) { + int *stored_ptr = list_inspect(l, p); + int stored_value = *stored_ptr; + + if (!value_equal(n - i, stored_value)) { + // Fail with error message + fprintf(stderr, "FAIL: expected %d, got %d after " + "insert(first())\n", n - i, stored_value); + exit(EXIT_FAILURE); + } + p = list_next(l, p); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/** + * count_list_elements_forwards() - Failsafely count the number of elements in a list from front() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with next() from front() until end() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_forwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_first(l); + while (!list_pos_is_equal(l, p, list_end(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_next(l, p); + } + return n; +} + +/** + * count_list_elements_backwards() - Failsafely count the number of elements in a list from end() + * @l: A list with any number of elements + * @max: The maximum number of elements to visit + * + * Returns: The number of elements in the list if <= max, otherwise -1. + * + * Traverses the list with prev() from end() until first() is + * encountered. If more than max elements are encountered, bail out + * and return -1. + */ +int count_list_elements_backwards(const list *l, int max) +{ + int n = 0; + list_pos p = list_end(l); + while (!list_pos_is_equal(l, p, list_first(l))) { + n++; + if (n > max) { + // We've gone past the limit, bail out + return -1; + } + p = list_prev(l, p); + } + return n; +} + +/* + * insert_and_count_forwards() - Check element count from first() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void insert_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_forwards()..."); + + // Start with empty list + list *l = list_empty(NULL); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * insert_and_count_backwards() - Check element count from end() after repeated inserts + * Precondition: list_empty(), list_first(), list_end(), list_prev() works + */ +void insert_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting insert_and_count_backwards()..."); + + // Start with empty list + list *l = list_empty(NULL); + + for (int n=0; n<5; n++) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + // Make dynamic copy of value to insert + int *q = int_create(n); + + // Insert one more element + list_insert(l, q, list_first(l)); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_forwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_forwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_forwards()..."); + + // Create a five-element list + list *l = list_empty(NULL); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_forwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_forwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +/* + * remove_and_count_backwards() - Check element count from first() after repeated removes + * Precondition: list_empty(), list_first(), list_end(), list_next() works + */ +void remove_and_count_backwards(void) +{ + // Print a starting message + fprintf(stderr,"Starting remove_and_count_backwards()..."); + + // Create a five-element list + list *l = list_empty(NULL); + for (int i = 0; i < 5; i++) { + // Make dynamic copy of value to insert + int *q = int_create(i); + + list_insert(l, q, list_first(l)); + } + + for (int n=5; n>=0; n--) { + // Check the actual count. + // Use max=10 as a safeguard against infinite loops + int c = count_list_elements_backwards(l, 10); + if (c != n) { + // Fail with error message + fprintf(stderr, "FAIL: count_backwards returned %d elements, " + "expected %d\n", c, n); + exit(EXIT_FAILURE); + } + if (n > 0) { + // Free stored pointer before removing element + int *q = list_inspect(l, list_first(l)); + int_kill(q); + + // Remove one element unless the list is empty + list_remove(l, list_first(l)); + } + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + + // De-allocate all element values stored in the list before killing the list + l = kill_all_element_values(l); + list_kill(l); + fprintf(stderr,"done.\n"); +} + +int main(void) +{ + printf("%s, %s %s: Test program for the generic list.\n" + "Does not use any free-handler to handle deallocation of dynamic memory.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Uses code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + empty_is_empty(); + empty_first_end(); + one_element_list_is_nonempty(); + one_element_list_has_first_neq_end(); + insert_first_returns_correct_pos(); + inserted_element_has_correct_value(); + next_does_something(); + one_element_list_next_eq_end(); + prev_does_something(); + one_element_list_prev_end_eq_first(); + one_element_list_prev_is_inv_next(); + one_element_list_next_is_inv_prev(); + insert_remove_is_empty(); + insert_remove_returns_end(); + one_element_list_check_insert_first_pos(); + one_element_list_check_insert_end_pos(); + two_element_list_check_links(); + one_element_list_insert_end(); + one_element_list_insert_first(); + insert_and_count_forwards(); + insert_and_count_backwards(); + remove_and_count_forwards(); + remove_and_count_backwards(); + two_element_list_insert_end(); + two_element_list_insert_first(); + two_element_list_insert_middle(); + n_element_list_insert_end(); + n_element_list_insert_first(); + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU4/src/queue/.gitignore b/OU4/src/queue/.gitignore new file mode 100644 index 0000000..9fd76b1 --- /dev/null +++ b/OU4/src/queue/.gitignore @@ -0,0 +1,2 @@ +queue_example +queue_example_internal diff --git a/OU4/src/queue/Makefile b/OU4/src/queue/Makefile new file mode 100644 index 0000000..5795e44 --- /dev/null +++ b/OU4/src/queue/Makefile @@ -0,0 +1,49 @@ +MWE = queue_mwe1 queue_mwe2 queue_mwe1i queue_mwe2i queue_example queue_example_internal + +SRC = queue.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +queue_example_internal: queue_example_internal.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_example: queue_example.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe1: queue_mwe1.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe2: queue_mwe2.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe1i: queue_mwe1i.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +queue_mwe2i: queue_mwe2i.c queue.c ../list/list.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: queue_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: queue_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: queue_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: queue_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/queue/README.md b/OU4/src/queue/README.md new file mode 100644 index 0000000..f6746dd --- /dev/null +++ b/OU4/src/queue/README.md @@ -0,0 +1,11 @@ +# Kö +En implementation av ADT:n _Kö_ baserad på en dubbel-länkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + +# Minimal working example + +Se [queue_mwe1.c](queue_mwe1.c) och [queue_mwe2.c](queue_mwe2.c). diff --git a/OU4/src/queue/queue.c b/OU4/src/queue/queue.c new file mode 100644 index 0000000..3b73bc7 --- /dev/null +++ b/OU4/src/queue/queue.c @@ -0,0 +1,401 @@ +#include +#include +#include + +#include +#include + +/* + * Implementation of a generic queue for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.01 2024-01-16: Fix include to be with brackets, not citation marks. + * v2.0 2024-03-14: Added queue_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +/* + * The queue is implemented using the list abstract datatype. Almost + * everything is done by the list. + */ + +struct queue { + list *elements; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * queue_empty() - Create an empty queue. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new queue. + */ +queue *queue_empty(kill_function kill_func) +{ + // Allocate the queue head. + queue *q = calloc(1, sizeof(*q)); + // Create an empty list. + q->elements = list_empty(kill_func); + + return q; +} + +/** + * queue_is_empty() - Check if a queue is empty. + * @q: Queue to check. + * + * Returns: True if queue is empty, otherwise false. + */ +bool queue_is_empty(const queue *q) +{ + return list_is_empty(q->elements); +} + +/** + * queue_enqueue() - Put a value at the end of the queue. + * @q: Queue to manipulate. + * @v: Value (pointer) to be put in the queue. + * + * Returns: The modified queue. + */ +queue *queue_enqueue(queue *q, void *v) +{ + list_insert(q->elements, v, list_end(q->elements)); + return q; +} + +/** + * queue_dequeue() - Remove the element at the front of a queue. + * @q: Queue to manipulate. + * + * NOTE: Undefined for an empty queue. + * + * Returns: The modified queue. + */ +queue *queue_dequeue(queue *q) +{ + list_remove(q->elements, list_first(q->elements)); + return q; +} + +/** + * queue_front() - Inspect the value at the front of the queue. + * @q: Queue to inspect. + * + * Returns: The value at the top of the queue. + * NOTE: The return value is undefined for an empty queue. + */ +void *queue_front(const queue *q) +{ + return list_inspect(q->elements, list_first(q->elements)); +} + +/** + * queue_kill() - Destroy a given queue. + * @q: Queue to destroy. + * + * Return all dynamic memory used by the queue and its elements. If a + * kill_func was registered at queue creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void queue_kill(queue *q) +{ + list_kill(q->elements); + free(q); +} + +/** + * queue_print() - Iterate over the queue elements and print their values. + * @q: Queue to inspect. + * @print_func: Function called for each element. + * + * Iterates over the queue and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void queue_print(const queue *q, inspect_callback print_func) +{ + printf("{ "); + list_pos pos = list_first(q->elements); + while (pos != list_end(q->elements)) { + print_func(list_inspect(q->elements, pos)); + pos = list_next(q->elements, pos); + if (pos != list_end(q->elements)) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the queue head struct. + * @il: Indentation level. + * @q: Queue to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const queue *q) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"elements\\n%04lx\" xlabel=\"%04lx\"]\n", + PTR2ADDR(q), PTR2ADDR(q->elements), PTR2ADDR(q)); +} + +// Print edges from the queue head to the elements list. +static void print_head_edge(int indent_level, const queue *q) +{ + print_edge(indent_level, q, q->elements, "e", "elements", NULL); +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * queue_print_internal() - Print the queues internal structure in dot format. + * @l: Queue to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the queue, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the queue and outputs dot code that shows the internal + * structure of the queue. The dot code can be visualized by + * Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph QUEUE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "\tcluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "q [label=\"%04lx\" xlabel=\"q\"]\n", PTR2ADDR(q)); + iprintf(il, "q -> m%04lx\n", PTR2ADDR(q)); + } + + // Output the payload nodes before asking the list to print them + // to render them in their own subgraph. + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + list_pos p = list_first(q->elements); + while (!list_pos_is_equal(q->elements, p, list_end(q->elements))) { + print_value_node(il, list_inspect(q->elements, p), print_func); + // Advance + p = list_next(q->elements, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_queue_%d { label=\"Queue\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, q); + + if (q->elements) { + list_print_internal(q->elements, print_func, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Output the edges from the head + print_head_edge(il, q); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/queue/queue_example.c b/OU4/src/queue/queue_example.c new file mode 100644 index 0000000..9782f9b --- /dev/null +++ b/OU4/src/queue/queue_example.c @@ -0,0 +1,68 @@ +#include +#include + +#include + +/* + * A small example that uses the generic queue to store integers. + * Based on int_queue_example.c from int_queue. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-23: First public version. + */ + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + // Create the queue. + queue *q = queue_empty(int_kill); + + // The queue should be empty. Is it? + if (queue_is_empty(q)) { + printf("The newly created queue is empty.\n"); + } else { + printf("The newly created queue is NOT empty.\n"); + } + + for (int i = 11; i <= 13; i++) { + printf("Add the value %d at the end of the queue.\n", i); + int *v = int_create(i); + // Put some values in the queue. + q = queue_enqueue(q, v); + } + + int *v = queue_front(q); + printf("The value at the front of the queue is: %d.\n", *v); + + // Remove one element from the queue. + printf("Remove one element.\n"); + q = queue_dequeue(q); + + v = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", *v); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/queue/queue_example_internal.c b/OU4/src/queue/queue_example_internal.c new file mode 100644 index 0000000..93d77e4 --- /dev/null +++ b/OU4/src/queue/queue_example_internal.c @@ -0,0 +1,124 @@ +#include +#include + +#include + +// Size of internal string buffer +#define BUFLEN 400 + +/* + * A small example that uses a generic queue to store integers. + * + * Generates two kinds of output: + * 1) The 'outside' view showing the stored values only. + * 2) The 'inside' view showing the internal organization. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2025-01-24: First public version. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2025-01-24" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print a message, then the queue and wait for the user to press return. +void print_and_wait(const queue *q, const char *msg) +{ + printf("\nThe output from queue_print() shows the values stored in the queue\n"); + printf("\n%s\n", msg); + queue_print(q, print_int); + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +// Print cut lines before and after a call array_print_internal. +void print_internal_and_wait(const queue *q, const char *desc) +{ + // Print starting marker line. + printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n"); + printf("\n\n1) Copy the lines between the cut marks\n" + "2) Paste into the left half of the browser window.\n" + "3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n"); + printf("\n--- CUT HERE ---\n\n"); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("\n--- END CUT HERE ---\n\n"); + + printf("\nPress Enter to continue..."); + getchar(); // Waits for the user to press Enter + printf("\n"); +} + +int main(void) +{ + // Message buffer + char buf[BUFLEN]; + + printf("%s, %s %s: Example use of a typed integer queue.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q = queue_empty(int_kill); + + // Call print functions to show the content of the queue + print_and_wait(q, "The queue is empty"); + print_internal_and_wait(q, "The queue is empty"); + + for (int i = 11; i <= 13; i++) { + int *v = int_create(i); + q = queue_enqueue(q, v); + snprintf(buf, BUFLEN, "The value %d was added to the queue", i); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + } + + int *w = queue_front(q); + printf("The value at the front of the queue is: %d.\n", *w); + + // Remove one element from the queue. + q = queue_dequeue(q); + snprintf(buf, BUFLEN, "The front element was removed from the queue"); + print_and_wait(q, buf); + print_internal_and_wait(q, buf); + + w = queue_front(q); + printf("The value at the front of the queue is now: %d.\n", *w); + + // Kill queue. Strictly not necessary for int_queue since it + // does not use any dynamic memory. Included for symmetry with + // queue.c. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/queue/queue_mwe1.c b/OU4/src/queue/queue_mwe1.c new file mode 100644 index 0000000..c807911 --- /dev/null +++ b/OU4/src/queue/queue_mwe1.c @@ -0,0 +1,89 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer queue without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q=queue_empty(NULL); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q=queue_enqueue(q,v); + } + + printf("--QUEUE before dequeueing--\n"); + queue_print(q, print_int); + + // Get value on top of queue. + int *v=queue_front(q); + // Remote element from queue. + q=queue_dequeue(q); + // Return memory + int_kill(v); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q, print_int); + + // Now we must empty the queue and free each value ourselves. + while (!queue_is_empty(q)) { + // Get value from top of queue. + int *v=queue_front(q); + // Remove element from queue. + q=queue_dequeue(q); + // Free value + int_kill(v); + } + // Finally, destroy the bare queue. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/queue/queue_mwe1i.c b/OU4/src/queue/queue_mwe1i.c new file mode 100644 index 0000000..d8f0bed --- /dev/null +++ b/OU4/src/queue/queue_mwe1i.c @@ -0,0 +1,134 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c that shows how the internal + * structure of a queue can be visualized. In this version, the queue + * "borrows" the payload memory, i.e., the user of the queue is + * responsible for deallocating the payload memory. See queue_mwe2i.c + * for a version where the queue takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call queue_print_internal. +void print_internal_with_cut_lines(const queue *q, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer queue without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue *q = queue_empty(NULL); + + printf("Empty queue from the outside:\n"); + queue_print(q, print_int); + print_internal_with_cut_lines(q, "Empty queue showing the internal structure"); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q = queue_enqueue(q, v); + } + + printf("Queue from the outside after inserting 3 values:\n"); + queue_print(q, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Queue after inserting 3 values.\n" + "All data is stored internally using a List.\n" + "Red lines are used for the queue payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the queue, i.e., the payload\n" + "memory will NOT be deallocated by the queue.\n\n" + "See queue_mwe2i for a queue example\nthat owns the payload memory."; + print_internal_with_cut_lines(q, long_desc); + + // Now we must empty the queue and free each value ourselves. + while (!queue_is_empty(q)) { + // Get value from top of queue. + int *v = queue_front(q); + // Remove element from queue. + q = queue_dequeue(q); + // Free value + int_kill(v); + } + // Finally, destroy the bare queue. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/queue/queue_mwe2.c b/OU4/src/queue/queue_mwe2.c new file mode 100644 index 0000000..ad27458 --- /dev/null +++ b/OU4/src/queue/queue_mwe2.c @@ -0,0 +1,75 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v=malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p=v; + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer queue with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the queue. + queue *q = queue_empty(int_kill); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q=queue_enqueue(q,v); + } + + printf("--QUEUE before dequeueing--\n"); + queue_print(q, print_int); + + // Dequeue one element. The payload memory will be automatically returned. + queue_dequeue(q); + + printf("--QUEUE after dequeueing--\n"); + queue_print(q, print_int); + + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/queue/queue_mwe2i.c b/OU4/src/queue/queue_mwe2i.c new file mode 100644 index 0000000..93d851a --- /dev/null +++ b/OU4/src/queue/queue_mwe2i.c @@ -0,0 +1,126 @@ +#include +#include + +#include + +/* + * Minimum working example for queue.c that shows how the internal + * structure of a queue can be visualized. In this version, the queue + * "owns" the payload memory, i.e., the queue takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See queue_mwe1i.c for a version + * where the queue does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call queue_print_internal. +void print_internal_with_cut_lines(const queue *q, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + queue_print_internal(q, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer queue with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the queue. + queue *q = queue_empty(int_kill); + + printf("Empty queue from the outside:\n"); + queue_print(q, print_int); + print_internal_with_cut_lines(q, "Empty queue showing the internal structure"); + + for (int i = 11; i <= 13; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Put value in queue. + q = queue_enqueue(q, v); + } + + printf("Queue from the outside after inserting 3 values:\n"); + queue_print(q, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Queue after inserting 3 values.\n" + "All data is stored internally using a List.\n" + "Red lines are used for the queue payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the queue, i.e., the payload\n" + "memory WILL be deallocated by the queue.\n\n" + "See queue_mwe1i for a queue example\nthat borrows the payload memory."; + print_internal_with_cut_lines(q, long_desc); + + // Destroy the queue and its content. + queue_kill(q); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/stack/Makefile b/OU4/src/stack/Makefile new file mode 100644 index 0000000..d2db60b --- /dev/null +++ b/OU4/src/stack/Makefile @@ -0,0 +1,43 @@ +MWE = stack_mwe1 stack_mwe2 stack_mwe1i stack_mwe2i + +SRC = stack.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +stack_mwe1: stack_mwe1.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe2: stack_mwe2.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe1i: stack_mwe1i.c stack.c + gcc -o $@ $(CFLAGS) $^ + +stack_mwe2i: stack_mwe2i.c stack.c + gcc -o $@ $(CFLAGS) $^ + +memtest1: stack_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest2: stack_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest3: stack_mwe1i + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest4: stack_mwe2i + valgrind --leak-check=full --show-reachable=yes ./$< diff --git a/OU4/src/stack/README.md b/OU4/src/stack/README.md new file mode 100644 index 0000000..0322013 --- /dev/null +++ b/OU4/src/stack/README.md @@ -0,0 +1,11 @@ +# Stack +En implementation av ADT:n _Stack_ implementerad som en enkellänkad lista. + +## Minneshantering och utskrift + +Det mesta av hur gränsytan används med avseende på minneshantering och +utskrifter är analogt för hur [listimplementationen](../list/) fungerar. + +# Minimal working example + +Se [stack_mwe1.c](stack_mwe1.c) och [stack_mwe2.c](stack_mwe2.c). diff --git a/OU4/src/stack/int_stack_test_marc.c b/OU4/src/stack/int_stack_test_marc.c new file mode 100644 index 0000000..c99cd2f --- /dev/null +++ b/OU4/src/stack/int_stack_test_marc.c @@ -0,0 +1,77 @@ +#include +#include +#include // for EXIT_FAILURE +/*#include */ + +#include +/*#include */ + +// Macro to compute length of a C array +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +/* + * Stub test program for the list implementation in list.c. + * + * Author: Niclas Borlin (niclas.borlin@cs.umu.se). + * + * Version information: + * 2018-01-28: v1.0. First public version. + * 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure. + * 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for + * int_list. + * 2023-01-21: v2.01. Updated print identification command. + * 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal. + * 2023-03-23: v2.2. Removed empty if statements in test code. + */ + +#define VERSION "v2.2" +#define VERSION_DATE "2023-03-23" + +/* + * Function to compare the values stored in the list. + */ +bool value_equal(int v1, int v2) +{ + return v1 == v2; +} + +/* + * empty_returns_non_null() - Test that the stack_empty() function returns a non-null pointer. + * Precondition: None. + */ +void empty_returns_non_null(void) +{ + // Print a starting message + fprintf(stderr,"Starting empty_returns_non_null()..."); + + // create an empty stack + stack s = stack_empty(); + + + // l should be non-NULL + if (s == NULL) { + // Fail with error message + fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n"); + exit(EXIT_FAILURE); + } + + // Everything went well, clean up + fprintf(stderr,"cleaning up..."); + list_kill(s); + fprintf(stderr,"done.\n"); +} + + + +int main(void) +{ + printf("%s, %s %s: Test program for the typed int_stack datatype.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s.\n\n", CODE_BASE_VERSION); + + empty_returns_non_null(); + + + fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n"); + return 0; +} diff --git a/OU4/src/stack/stack.c b/OU4/src/stack/stack.c new file mode 100644 index 0000000..cd3a869 --- /dev/null +++ b/OU4/src/stack/stack.c @@ -0,0 +1,463 @@ +#include +#include +#include + +#include + +/* + * Implementation of a generic stack for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2022-03-24: Minor update to always take care of returned pointer. + * v2.0 2024-03-14: Added stack_print_internal to output dot code for visualization. + * Renamed free_* stuff to kill_*. Converted to 4-tabs. + * v2.1 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES============ + +/* + * The stack elements are implemented as one-cells with forward and + * links. The stack has a single element pointer. + */ +typedef struct cell { + struct cell *next; + void *val; +} cell; + +struct stack { + cell *top; + kill_function kill_func; +}; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS============ + +/** + * stack_empty() - Create an empty stack. + * @kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory on remove/kill. + * + * Returns: A pointer to the new stack. + */ +stack *stack_empty(kill_function kill_func) +{ + // Allocate memory for stack structure. + stack *s = calloc(1, sizeof(stack)); + s->top = NULL; + s->kill_func = kill_func; + + return s; +} + +/** + * stack_is_empty() - Check if a stack is empty. + * @s: Stack to check. + * + * Returns: True if stack is empty, otherwise false. + */ +bool stack_is_empty(const stack *s) +{ + return s->top == NULL; +} + +/** + * stack_push() - Push a value on top of a stack. + * @s: Stack to manipulate. + * @v: Value (pointer) to be put on the stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_push(stack *s, void *v) +{ + // Allocate memory for element. + cell *e = calloc(1, sizeof(*e)); + // Set element value. + e->val = v; + // Link to current top. + e->next = s->top; + // Put element on top of stack. + s->top = e; + // Return modified stack. + return s; +} + +/** + * stack_pop() - Remove the element at the top of a stack. + * @s: Stack to manipulate. + * + * NOTE: Undefined for an empty stack. + * + * Returns: The modified stack. + * NOTE: After the call, the input stack should be considered invalid. + */ +stack *stack_pop(stack *s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_pop: Warning: pop on empty stack\n"); + } else { + // Remember top element. + cell *e = s->top; + // Link past top element. + s->top = s->top->next; + // De-allocate user memory. + if (s->kill_func != NULL) { + s->kill_func(e->val); + } + // De-allocate element memory. + free(e); + } + return s; +} + +/** + * stack_top() - Inspect the value at the top of the stack. + * @s: Stack to inspect. + * + * Returns: The value at the top of the stack. + * NOTE: The return value is undefined for an empty stack. + */ +void *stack_top(const stack *s) +{ + if (stack_is_empty(s)) { + fprintf(stderr, "stack_top: Warning: top on empty stack\n"); + } + return s->top->val; +} + +/** + * stack_kill() - Destroy a given stack. + * @s: Stack to destroy. + * + * Return all dynamic memory used by the stack and its elements. If a + * kill_func was registered at stack creation, also calls it for each + * element to kill any user-allocated memory occupied by the element values. + * + * Returns: Nothing. + */ +void stack_kill(stack *s) +{ + while (!stack_is_empty(s)) { + s = stack_pop(s); + } + free(s); +} + +/** + * stack_print() - Iterate over the stack elements and print their values. + * @s: Stack to inspect. + * @print_func: Function called for each element. + * + * Iterates over the stack and calls print_func with the value stored + * in each element. + * + * Returns: Nothing. + */ +void stack_print(const stack *s, inspect_callback print_func) +{ + printf("{ "); + cell *e = s->top; + while (e != NULL) { + print_func(e->val); + e = e->next; + if (e != NULL) { + printf(", "); + } + } + printf(" }\n"); +} + +// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the stack head struct. + * @indent_level: Indentation level. + * @s: Stack to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const stack *s) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"kill\\n%04lx|top\\n%04lx\"]\n", + PTR2ADDR(s), PTR2ADDR(s->kill_func), PTR2ADDR(s->top)); +} + +// Print edges from the stack head to the head cell. +static void print_head_edge(int indent_level, const stack *s) +{ + print_edge(indent_level, s, s->top, "t", "top", NULL); +} + +// Print a node corresponding to the cell at position p. +static void print_elem_node(int indent_level, const cell *p) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"val\\n%04lx|next\\n%04lx\"]\n", + PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next)); +} + +// Print edges from the cell at position p to the next and previous +// cells and the value. The value pointer is red, the others are +// black. If the stack owns the memory, the edge is solid, otherwise +// dashed. +static void print_elem_edges(int indent_level, const stack *s, const cell *p) +{ + print_edge(indent_level, p, p->next, "n", "next", NULL); + + if (s->kill_func) { + print_edge(indent_level, p, p->val, "v", "val", "color=red"); + } else { + print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed"); + } +} + +// Print the node for the memory block at p using the user-supplied +// print_func to print the label. +static void print_value_node(int indent_level, const void *p, inspect_callback print_func) +{ + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p)); + if (print_func != NULL) { + print_func(p); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p)); +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * stack_print_internal() - Print the stacks internal structure in dot format. + * @l: Stack to inspect. + * @print_func: Function called for each element value. + * @desc: String with a description/state of the stack, or NULL for no description. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the stack and outputs dot code that shows the internal + * structure of the stack. The dot code can be visualized by Graphviz. + * + * On linux system, the output can be parsed by the dot program, e.g. + * + * | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg + * + * where is the name of the executable + * + * The output may also be possible to visualize online on + * https://dreampuf.github.io/GraphvizOnline/ or google "graphviz + * online". + * + * For documention of the dot language, see graphviz.org. + * + * Returns: Nothing. + */ +void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph... + printf("digraph STACK_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", escaped); + } else { + iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped); + } + // Return the memory used by the escaped string + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "s [label=\"%04lx\" xlabel=\"s\"]\n", PTR2ADDR(s)); + iprintf(il, "s -> m%04lx\n", PTR2ADDR(s)); + } + + // Print the subgraph to surround the Stack content + iprintf(il, "subgraph cluster_stack_%d { label=\"Stack\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, s); + + // Output the element nodes + cell *p = s->top; + while (p != NULL) { + print_elem_node(il, p); + p = p->next; + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + } + + // Output the value nodes + p = s->top; + while (p != NULL) { + if (p->val) { + print_value_node(il, p->val, print_func); + } + p = p->next; + } + + if (indent_level == 0) { + // Close userspace + il--; + iprintf(il, "}\n"); + } + + // Output the edges from the head + print_head_edge(il, s); + + // Output the edges from each element + p = s->top; + while (p != NULL) { + print_elem_edges(il, s, p); + p = p->next; + } + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/stack/stack_mwe1.c b/OU4/src/stack/stack_mwe1.c new file mode 100644 index 0000000..eba1d03 --- /dev/null +++ b/OU4/src/stack/stack_mwe1.c @@ -0,0 +1,89 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2023-01-14: Added printouts at start/end of main. + * v1.3 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.3" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer stack without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. + stack *s = stack_empty(NULL); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("--STACK before popping--\n"); + stack_print(s, print_int); + + // Get value on top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + + printf("--STACK after popping--\n"); + stack_print(s, print_int); + + // Now we must empty the stack and free each value ourselves. + while (!stack_is_empty(s)) { + // Get value from top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + } + // Finally, destroy the bare stack. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/stack/stack_mwe1i.c b/OU4/src/stack/stack_mwe1i.c new file mode 100644 index 0000000..c1968a8 --- /dev/null +++ b/OU4/src/stack/stack_mwe1i.c @@ -0,0 +1,133 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c that shows how the internal + * structure of a stack can be visualized. In this version, the stack + * "borrows" the payload memory, i.e., the user of the stack is + * responsible for deallocating the payload memory. See stack_mwe2i.c + * for a version where the stack takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call stack_print_internal. +void print_internal_with_cut_lines(const stack *s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer stack without kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack *s = stack_empty(NULL); + + printf("Empty stack from the outside:\n"); + stack_print(s, print_int); + print_internal_with_cut_lines(s, "Empty stack showing the internal structure"); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("Stack from the outside after pushing 3 values:\n"); + stack_print(s, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Stack after pushing 3 values.\n" + "Red lines are used for the stack payload.\n\n" + "The dashed red lines indicate that the payload memory is\n" + "BORROWED by the stack, i.e., the payload\n" + "memory will NOT be deallocated by the stack.\n\n" + "See stack_mwe2i for a stack example\nthat owns the payload memory."; + print_internal_with_cut_lines(s, long_desc); + + // Now we must empty the stack and free each value ourselves. + while (!stack_is_empty(s)) { + // Get value from top of stack. + int *v = stack_top(s); + // Pop element from stack. + s = stack_pop(s); + // Return memory used by int + int_kill(v); + } + // Finally, destroy the bare stack. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/stack/stack_mwe2.c b/OU4/src/stack/stack_mwe2.c new file mode 100644 index 0000000..fe4f01c --- /dev/null +++ b/OU4/src/stack/stack_mwe2.c @@ -0,0 +1,78 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-01-28: First public version. + * v1.1 2018-04-03: Split into versions with/without memhandler. + * v1.2 2022-03-24: Update to always take care of returned pointer. + * v1.3 2023-01-14: Added printouts at start/end of main. + * v1.4 2024-03-14: Added explicit create/kill functions. + */ + +#define VERSION "v1.4" +#define VERSION_DATE "2024-03-14" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +int main(void) +{ + printf("%s, %s %s: Create integer stack with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Create the stack. Make the stack responsible for + // deallocation pushed values. + stack *s = stack_empty(int_kill); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("--STACK before popping--\n"); + stack_print(s, print_int); + + // Pop'ing will automatically deallocate the payload + s = stack_pop(s); + + printf("--STACK after popping--\n"); + stack_print(s, print_int); + + // Killing will automatically deallocate the payload + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/stack/stack_mwe2i.c b/OU4/src/stack/stack_mwe2i.c new file mode 100644 index 0000000..b1d63bc --- /dev/null +++ b/OU4/src/stack/stack_mwe2i.c @@ -0,0 +1,125 @@ +#include +#include + +#include + +/* + * Minimum working example for stack.c that shows how the internal + * structure of a stack can be visualized. In this version, the stack + * "owns" the payload memory, i.e., the stack takes over the + * responsibility to deallocate the payload memory when the + * corresponding elements are removed. See stack_mwe1i.c for a version + * where the stack does not take over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-03-14: First public version. + * v1.1 2024-03-15: Moved printout of dot instructions to separate function. + */ + +#define VERSION "v1.1" +#define VERSION_DATE "2024-03-15" + +// Integers are stored via int pointers stored as void pointers. +// Convert the given pointer and print the dereferenced value. +void print_int(const void *data) +{ + const int *v = data; + printf("%d", *v); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; // Convert to a readable pointer - useful in a debugger + free(p); +} + +// Print cut lines before and after a call stack_print_internal. +void print_internal_with_cut_lines(const stack *s, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + stack_print_internal(s, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create integer stack with kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Create the stack. + stack *s = stack_empty(int_kill); + + printf("Empty stack from the outside:\n"); + stack_print(s, print_int); + print_internal_with_cut_lines(s, "Empty stack showing the internal structure"); + + for (int i=1; i<=3; i++) { + // Allocate memory for one int. + int *v = int_create(i); + // Push value on stack. + s = stack_push(s, v); + } + + printf("Stack from the outside after pushing 3 values:\n"); + stack_print(s, print_int); + const char *long_desc = __FILE__ + ": Internal structure of the Stack after pushing 3 values.\n" + "Red lines are used for the stack payload.\n\n" + "The solid red lines indicate that the payload memory is\n" + "OWNED -- not borrowed -- by the stack, i.e., the payload\n" + "memory WILL be deallocated by the stack.\n\n" + "See stack_mwe1i for a stack example\nthat borrows the payload memory."; + print_internal_with_cut_lines(s, long_desc); + + // Kill the stack. The payload memory is returned automatically. + stack_kill(s); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/table/Makefile b/OU4/src/table/Makefile new file mode 100644 index 0000000..e46509a --- /dev/null +++ b/OU4/src/table/Makefile @@ -0,0 +1,68 @@ +MWE = table_mwe1 table_mwe2 table_mwe3 table2_mwe1 table2_mwe2 table2_mwe3 table_mwe1i table_mwe2i table2_mwe1i table2_mwe2i + +SRC = table.c +OBJ = $(SRC:.c=.o) + +CC = gcc +CFLAGS = -std=c99 -Wall -I../../include -g + +all: mwe + +# Minimum working examples. +mwe: $(MWE) + +# Object file for library +obj: $(OBJ) + +# Clean up +clean: + -rm -f $(MWE) $(OBJ) + +table_mwe1: table_mwe1.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe2: table_mwe2.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe3: table_mwe3.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe1i: table_mwe1i.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table_mwe2i: table_mwe2i.c table.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe1: table_mwe1.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe2: table_mwe2.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe3: table_mwe3.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe1i: table_mwe1i.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +table2_mwe2i: table_mwe2i.c table2.c ../dlist/dlist.c + gcc -o $@ $(CFLAGS) $^ + +memtest11: table_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest12: table_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest13: table_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest21: table2_mwe1 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest22: table2_mwe2 + valgrind --leak-check=full --show-reachable=yes ./$< + +memtest23: table2_mwe3 + valgrind --leak-check=full --show-reachable=yes ./$< + diff --git a/OU4/src/table/README.md b/OU4/src/table/README.md new file mode 100644 index 0000000..752c86f --- /dev/null +++ b/OU4/src/table/README.md @@ -0,0 +1,125 @@ +# Tabellimplementation + +Det som skiljer tabellen från de övriga datatyperna är att vi här måste hantera +både nycklar och värden. Denna hantering fungerar analogt med de övriga, men vi +konkretiserar ändå med ett par exempel på hur tabellen kan användas. + +## Jämförelsefunktion + +För att vår tabell ska fungera behöver vi specificera hur nycklar ska jämföras. +Detta tillåter oss att t.ex. använda strängar eller heltal som nycklar utan att +den underliggande implementationen behöver ändras. Detta bygger på en +`compare_function`, som tar två pekare och returnerar `0` ifall de är lika, `0>` +om första värdet är större än det andra, samt `0<` om första värdet är mindre än +det andra. I fallet med heltal kan detta se ut såhär: + +```c +static int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} +``` + +Vi kan då skapa en tabell med `table_empty(compare_ints, free, free) som vi +sedan kan använda såhär: + +```c +table *t = table_empty(compare_ints, free, free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); + +int key_to_lookup = 5; +table_lookup(t, &key_to_lookup); +``` + +Om vår `compare_function` är felaktigt implementerad så kommer inte +`table_lookup` hitta värdet, trots att vi stoppat in det. + +## Minneshantering + +När vi skapar en tabell måste vi specificera hur minnet för nycklar och värden +ska hanteras. Ofta kommer vi vilja allokera minne på heapen dynamiskt, och låta +tabellen avallokera både nyklar och värden: + +```c +table *t = table_empty(compare_ints, free, free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); +table_kill(t); +``` + +Vi skulle även kunna låta nycklarna ligga på stacken, och bara allokera värden +på heapen enl. följande exempel: + +```c +table *t = table_empty(compare_ints, NULL, free); + +int key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, &key, value); +table_kill(t); +``` + +Det skulle också kunna vara så att nycklarna allokeras dynamiskt, men att de +också stoppas i en annan datatyp (t.ex. en lista). Följande kodsnutt illustrera +det exemplet: + +```c +table *t = table_empty(compare_ints, NULL, free); +list *l = list_empty(free); + +int *key = malloc(sizeof(int)); +*key = 5; +char *value = calloc(5, sizeof(char)); +strcpy(value, "test"); + +table_insert(t, key, value); +list_insert(l, key, list_first(l)); + +// Kommer inte frigöra key, bara value +table_kill(t); + +// Frigör också key +list_kill(l); +``` + +## Utskrift + +Till skillnad från de övriga datatyperna så vill vi skriva ut både nyckeln och +värdet med vår print-funktion. Istället för att exponera den interna `struct`:en +`table_entry` utanför c-filen, så använder vi oss istället av en print-funktion +som tar två parameterar. + +```c +static void print_int_string_pair(const void *key, const void *value) +{ + printf("[%d, %s]\n", *(int*)key, (char*)value); +} +``` + +Ett anrop till `table_print(t, print_int_string_pair)` skulle då skriva ut `[5, + test]` om vi utgår ifrån det tidigare exemplet. + + +# Minimal Working Example + +Se [table_mwe1.c](table_mwe1.c), [table_mwe2.c](table_mwe2.c) och [table_mwe3.c](table_mwe3.c). diff --git a/OU4/src/table/table.c b/OU4/src/table/table.c new file mode 100644 index 0000000..926e916 --- /dev/null +++ b/OU4/src/table/table.c @@ -0,0 +1,687 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2019-03-04: Bugfix in table_remove. + * v1.2 2024-04-15: Added table_print_internal. + * v2.0 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(table_entry_kill); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + // Iterate over the list. Return first match. + + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + // If yes, return the corresponding value pointer. + return e->value; + } + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/table/table2.c b/OU4/src/table/table2.c new file mode 100644 index 0000000..c073ee5 --- /dev/null +++ b/OU4/src/table/table2.c @@ -0,0 +1,692 @@ +#include +#include +#include +#include // For isspace() +#include + +#include +#include + +/* + * Implementation of a generic table for the "Datastructures and + * algorithms" courses at the Department of Computing Science, Umea + * University. + * + * Duplicates are handled by inspect and remove. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Based on earlier code by: Johan Eliasson (johane@cs.umu.se). + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2019-02-21: Second version without dlist/memfreehandler. + * v1.2 2019-03-04: Bugfix in table_remove. + * v1.3 2024-04-15: Added table_print_internal. + * v2.0 2024-05-10: Updated print_internal with improved encapsulation. + */ + +// ===========INTERNAL DATA TYPES ============ + +struct table { + dlist *entries; // The table entries are stored in a directed list + compare_function *key_cmp_func; + kill_function key_kill_func; + kill_function value_kill_func; +}; + +typedef struct table_entry { + void *key; + void *value; +} table_entry; + +// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============ + +/** + * table_entry_create() - Allocate and populate a table entry. + * @key: A pointer to a function to be used to compare keys. + * @value: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * + * Returns: A pointer to the newly created table entry. + */ +table_entry *table_entry_create(void *key, void *value) +{ + // Allocate space for a table entry. Use calloc as a defensive + // measure to ensure that all pointers are initialized to NULL. + table_entry *e = calloc(1, sizeof(*e)); + // Populate the entry. + e->key = key; + e->value = value; + + return e; +} + +/** + * table_entry_kill() - Return the memory allocated to a table entry. + * @e: The table entry to deallocate. + * + * Returns: Nothing. + */ +void table_entry_kill(void *v) +{ + table_entry *e = v; // Convert the pointer (useful if debugging the code) + + // All we need to do is to deallocate the struct. + free(e); +} + +/** + * table_empty() - Create an empty table. + * @key_cmp_func: A pointer to a function to be used to compare keys. + * @key_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for keys on remove/kill. + * @value_kill_func: A pointer to a function (or NULL) to be called to + * de-allocate memory for values on remove/kill. + * + * Returns: Pointer to a new table. + */ +table *table_empty(compare_function *key_cmp_func, + kill_function key_kill_func, + kill_function value_kill_func) +{ + // Allocate the table header. + table *t = calloc(1, sizeof(table)); + // Create the list to hold the table_entry-ies. + t->entries = dlist_empty(NULL); + // Store the key compare function and key/value kill functions. + t->key_cmp_func = key_cmp_func; + t->key_kill_func = key_kill_func; + t->value_kill_func = value_kill_func; + + return t; +} + +/** + * table_is_empty() - Check if a table is empty. + * @table: Table to check. + * + * Returns: True if table contains no key/value pairs, false otherwise. + */ +bool table_is_empty(const table *t) +{ + return dlist_is_empty(t->entries); +} + +/** + * table_insert() - Add a key/value pair to a table. + * @table: Table to manipulate. + * @key: A pointer to the key value. + * @value: A pointer to the value value. + * + * Insert the key/value pair into the table. No test is performed to + * check if key is a duplicate. table_lookup() will return the latest + * added value for a duplicate key. table_remove() will remove all + * duplicates for a given key. + * + * Returns: Nothing. + */ +void table_insert(table *t, void *key, void *value) +{ + // Allocate the key/value structure. + table_entry *e = table_entry_create(key, value); + + dlist_insert(t->entries, e, dlist_first(t->entries)); +} + +/** + * table_lookup() - Look up a given key in a table. + * @table: Table to inspect. + * @key: Key to look up. + * + * Returns: The value corresponding to a given key, or NULL if the key + * is not found in the table. If the table contains duplicate keys, + * the value that was latest inserted will be returned. + */ +void *table_lookup(const table *t, const void *key) +{ + // Iterate over the list. Return first match. + + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + // Check if the entry key matches the search key. + if (t->key_cmp_func(e->key, key) == 0) { + // If yes, return the corresponding value pointer. + return e->value; + } + // Continue with the next position. + pos = dlist_next(t->entries, pos); + } + // No match found. Return NULL. + return NULL; +} + +/** + * table_choose_key() - Return an arbitrary key. + * @t: Table to inspect. + * + * Return an arbitrary key stored in the table. Can be used together + * with table_remove() to deconstruct the table. Undefined for an + * empty table. + * + * Returns: An arbitrary key stored in the table. + */ +void *table_choose_key(const table *t) +{ + // Return first key value. + dlist_pos pos = dlist_first(t->entries); + table_entry *e = dlist_inspect(t->entries, pos); + + return e->key; +} + +/** + * table_remove() - Remove a key/value pair in the table. + * @table: Table to manipulate. + * @key: Key for which to remove pair. + * + * Any matching duplicates will be removed. Will call any kill + * functions set for keys/values. Does nothing if key is not found in + * the table. + * + * Returns: Nothing. + */ +void table_remove(table *t, const void *key) +{ + // Will be set if we need to delay a free. + void *deferred_ptr = NULL; + + // Start at beginning of the list. + dlist_pos pos = dlist_first(t->entries); + + // Iterate over the list. Remove any entries with matching keys. + while (!dlist_is_end(t->entries, pos)) { + // Inspect the table entry + table_entry *e = dlist_inspect(t->entries, pos); + + // Compare the supplied key with the key of this entry. + if (t->key_cmp_func(e->key, key) == 0) { + // If we have a match, call kill on the key + // and/or value if given the responsiblity + if (t->key_kill_func != NULL) { + if (e->key == key) { + // The given key points to the same + // memory as entry->key. Freeing it here + // would trigger a memory error in the + // next iteration. Instead, defer free + // of this pointer to the very end. + deferred_ptr = e->key; + } else { + t->key_kill_func(e->key); + } + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Remove the list element itself. + pos = dlist_remove(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } else { + // No match, move on to next element in the list. + pos = dlist_next(t->entries, pos); + } + } + if (deferred_ptr != NULL) { + // Take care of the delayed free. + t->key_kill_func(deferred_ptr); + } +} + +/* + * table_kill() - Destroy a table. + * @table: Table to destroy. + * + * Return all dynamic memory used by the table and its elements. If a + * kill_func was registered for keys and/or values at table creation, + * it is called each element to kill any user-allocated memory + * occupied by the element values. + * + * Returns: Nothing. + */ +void table_kill(table *t) +{ + // Iterate over the list. Destroy all elements. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + // Inspect the key/value pair. + table_entry *e = dlist_inspect(t->entries, pos); + // Kill key and/or value if given the authority to do so. + if (t->key_kill_func != NULL) { + t->key_kill_func(e->key); + } + if (t->value_kill_func != NULL) { + t->value_kill_func(e->value); + } + // Move on to next element. + pos = dlist_next(t->entries, pos); + // Deallocate the table entry structure. + table_entry_kill(e); + } + + // Kill what's left of the list... + dlist_kill(t->entries); + // ...and the table struct. + free(t); +} + +/** + * table_print() - Print the given table. + * @t: Table to print. + * @print_func: Function called for each key/value pair in the table. + * + * Iterates over the key/value pairs in the table and prints them. + * Will print all stored elements, including duplicates. + * + * Returns: Nothing. + */ +void table_print(const table *t, inspect_callback_pair print_func) +{ + // Iterate over all elements. Call print_func on keys/values. + dlist_pos pos = dlist_first(t->entries); + + while (!dlist_is_end(t->entries, pos)) { + table_entry *e = dlist_inspect(t->entries, pos); + // Call print_func + print_func(e->key, e->value); + pos = dlist_next(t->entries, pos); + } +} + +// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============ + +// The functions below output code in the dot language, used by +// GraphViz. For documention of the dot language, see graphviz.org. + +/** + * indent() - Output indentation string. + * @n: Indentation level. + * + * Print n tab characters. + * + * Returns: Nothing. + */ +static void indent(int n) +{ + for (int i=0; i ", PTR2ADDR(from), port); + } else { + printf("m%04lx -> ", PTR2ADDR(from)); + } + if (to == NULL) { + printf("NULL"); + } else { + printf("m%04lx", PTR2ADDR(to)); + } + printf(" ["); + if (options != NULL) { + printf("%s", options); + } + if (label != NULL) { + printf(" label=\"%s\"",label); + } + printf("]\n"); +} + +/** + * print_head_node() - Print a node corresponding to the table struct. + * @indent_level: Indentation level. + * @t: Table to inspect. + * + * Returns: Nothing. + */ +static void print_head_node(int indent_level, const table *t) +{ + iprintf(indent_level, "m%04lx [shape=record " + "label=\"entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n", + PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func), + PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func)); +} + +// Internal function to print the head--entries edge in dot format. +static void print_head_edge(int indent_level, const table *t) +{ + print_edge(indent_level, t, t->entries, "e", "entries", NULL); +} + +// Internal function to print the table entry node in dot format. +static void print_element_node(int indent_level, const table_entry *e) +{ + iprintf(indent_level, "m%04lx [shape=record label=\"key\\n%04lx|value\\n%04lx\"]\n", + PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value)); +} + +// Internal function to print the table entry node in dot format. +static void print_key_value_nodes(int indent_level, const table_entry *e, + inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (e->key != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key)); + if (key_print_func != NULL) { + key_print_func(e->key); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key)); + } + if (e->value != NULL) { + iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value)); + if (value_print_func != NULL) { + value_print_func(e->value); + } + printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value)); + } +} + +// Internal function to print edges from the table entry node in dot format. +// Memory "owned" by the table is indicated by solid red lines. Memory +// "borrowed" from the user is indicated by red dashed lines. +static void print_key_value_edges(int indent_level, const table *t, const table_entry *e) +{ + // Print the key edge + if (e->key == NULL) { + print_edge(indent_level, e, e->key, "k", "key", NULL); + } else { + if (t->key_kill_func) { + print_edge(indent_level, e, e->key, "k", "key", "color=red"); + } else { + print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed"); + } + } + + // Print the value edge + if (e->value == NULL) { + print_edge(indent_level, e, e->value, "v", "value", NULL); + } else { + if (t->value_kill_func) { + print_edge(indent_level, e, e->value, "v", "value", "color=red"); + } else { + print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed"); + } + } +} + +// Internal function to print nodes and edges of all table entries in dot format. +static void print_entries(int indent_level, const table *t, inspect_callback key_print_func, + inspect_callback value_print_func) +{ + if (t->entries == NULL) { + return; + } + dlist *l = t->entries; + dlist_pos p = dlist_first(l); + while (!dlist_is_end(l, p)) { + table_entry *e = dlist_inspect(l, p); + print_element_node(indent_level, e); + print_key_value_nodes(indent_level, e, key_print_func, value_print_func); + print_key_value_edges(indent_level, t, e); + p = dlist_next(l, p); + } +} + +// Create an escaped version of the input string. The most common +// control characters - newline, horizontal tab, backslash, and double +// quote - are replaced by their escape sequence. The returned pointer +// must be deallocated by the caller. +static char *escape_chars(const char *s) +{ + int i, j; + int escaped = 0; // The number of chars that must be escaped. + + // Count how many chars need to be escaped, i.e. how much longer + // the output string will be. + for (i = escaped = 0; s[i] != '\0'; i++) { + if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') { + escaped++; + } + } + // Allocate space for the escaped string. The variable i holds the input + // length, escaped how much the string will grow. + char *t = malloc(i + escaped + 1); + + // Copy-and-escape loop + for (i = j = 0; s[i] != '\0'; i++) { + // Convert each control character by its escape sequence. + // Non-control characters are copied as-is. + switch (s[i]) { + case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break; + case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break; + case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break; + case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break; + default: t[i+j] = s[i]; break; + } + } + // Terminal the output string + t[i+j] = '\0'; + return t; +} + +/** + * first_white_spc() - Return pointer to first white-space char. + * @s: String. + * + * Returns: A pointer to the first white-space char in s, or NULL if none is found. + * + */ +static const char *find_white_spc(const char *s) +{ + const char *t = s; + while (*t != '\0') { + if (isspace(*t)) { + // We found a white-space char, return a point to it. + return t; + } + // Advance to next char + t++; + } + // No white-space found + return NULL; +} + +/** + * insert_table_name() - Maybe insert the name of the table src file in the description string. + * @s: Description string. + * + * Parses the description string to find of if it starts with a c file + * name. In that case, the file name of this file is spliced into the + * description string. The parsing is not very intelligent: If the + * sequence ".c:" (case insensitive) is found before the first + * white-space, the string up to and including ".c" is taken to be a c + * file name. + * + * Returns: A dynamic copy of s, optionally including with the table src file name. + */ +static char *insert_table_name(const char *s) +{ + // First, determine if the description string starts with a c file name + // a) Search for the string ".c:" + const char *dot_c = strstr(s, ".c:"); + // b) Search for the first white-space + const char *spc = find_white_spc(s); + + bool prefix_found; + int output_length; + + // If both a) and b) are found AND a) is before b, we assume that + // s starts with a file name + if (dot_c != NULL && spc != NULL && dot_c < spc) { + // We found a match. Output string is input + 3 chars + __FILE__ + prefix_found = true; + output_length = strlen(s) + 3 + strlen(__FILE__); + } else { + // No match found. Output string is just input + prefix_found = false; + output_length = strlen(s); + } + + // Allocate space for the whole string + char *out = calloc(1, output_length + 1); + strcpy(out, s); + if (prefix_found) { + // Overwrite the output buffer from the ":" + strcpy(out + (dot_c - s + 2), " ("); + // Now out will be 0-terminated after "(", append the file name and ")" + strcat(out, __FILE__); + strcat(out, ")"); + // Finally append the input string from the : onwards + strcat(out, dot_c + 2); + } + return out; +} + +/** + * table_print_internal() - Output the internal structure of the table. + * @t: Table to print. + * @key_print_func: Function called for each key in the table. + * @value_print_func: Function called for each value in the table. + * @desc: String with a description/state of the list. + * @indent_level: Indentation level, 0 for outermost + * + * Iterates over the list and prints code that shows its' internal structure. + * + * Returns: Nothing. + */ +void table_print_internal(const table *t, inspect_callback key_print_func, + inspect_callback value_print_func, const char *desc, + int indent_level) +{ + static int graph_number = 0; + graph_number++; + int il = indent_level; + + if (indent_level == 0) { + // If this is the outermost datatype, start a graph and set up defaults + printf("digraph TABLE_%d {\n", graph_number); + + // Specify default shape and fontname + il++; + iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n"); + iprintf(il, "ranksep=0.01\n"); + iprintf(il, "subgraph cluster_nullspace {\n"); + iprintf(il+1, "NULL\n"); + iprintf(il, "}\n"); + } + + if (desc != NULL) { + // Escape the string before printout + char *escaped = escape_chars(desc); + // Optionally, splice the source file name + char *spliced = insert_table_name(escaped); + + // Use different names on inner description nodes + if (indent_level == 0) { + iprintf(il, "description [label=\"%s\"]\n", spliced); + } else { + iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced); + } + // Return the memory used by the spliced and escaped strings + free(spliced); + free(escaped); + } + + if (indent_level == 0) { + // Use a single "pointer" edge as a starting point for the + // outermost datatype + iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t)); + iprintf(il, "t -> m%04lx\n", PTR2ADDR(t)); + } + + if (indent_level == 0) { + // Put the user nodes in userspace + iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n"); + il++; + + // Iterate over the list to print the payload nodes + dlist_pos p = dlist_first(t->entries); + while (!dlist_is_end(t->entries, p)) { + print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func, + value_print_func); + // Advance + p = dlist_next(t->entries, p); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + } + + // Print the subgraph to surround the DList content + iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number); + il++; + + // Output the head node + print_head_node(il, t); + + // Output the edges from the head + print_head_edge(il, t); + + if (t->entries) { + // First, ask the dlist to output its internal structure. + dlist_print_internal(t->entries, NULL, NULL, il); + } + + // Close the subgraph + il--; + iprintf(il, "}\n"); + + // Next, print each element stored in the list + print_entries(il, t, key_print_func, value_print_func); + + if (indent_level == 0) { + // Termination of graph + printf("}\n"); + } +} diff --git a/OU4/src/table/table_mwe1.c b/OU4/src/table/table_mwe1.c new file mode 100644 index 0000000..5b7dac5 --- /dev/null +++ b/OU4/src/table/table_mwe1.c @@ -0,0 +1,135 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Makes two lookups and prints the + * result. The responsibility to deallocate the key-value pairs is NOT + * handed over to the table. Thus, all pointers must be stored outside + * the table. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.01 2019-03-05: Improved docs. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Keep track of the key-value pairs we allocate. + int *key[4]; + char *value[4]; + + // Keep the deallocation responsibility of the keys/values we allocate. + table *t = table_empty(compare_ints, NULL, NULL); + + key[0] = int_create(90187); + value[0] = string_copy("Umea"); + table_insert(t, key[0], value[0]); + + key[1] = int_create(90184); + value[1] = string_copy("Umea"); + table_insert(t, key[1], value[1]); + + key[2] = int_create(98185); + value[2] = string_copy("Kiruna"); + table_insert(t, key[2], value[2]); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + key[3] = int_create(90187); + value[3] = string_copy("Umea (Universitet)"); + table_insert(t, key[3], value[3]); + + printf("Table after adding a duplicate:\n"); + table_print(t, print_int_string_pair); + + v = 90187; + s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Kill the table, excluding the keys and values we entered. + table_kill(t); + + // Free key/value pairs that we put in the table. + for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) { + int_kill(key[i]); + string_kill(value[i]); + } + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/table/table_mwe1i.c b/OU4/src/table/table_mwe1i.c new file mode 100644 index 0000000..01e4bcb --- /dev/null +++ b/OU4/src/table/table_mwe1i.c @@ -0,0 +1,173 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Output dot/graphviz code to show + * the internal structure of the table. In this version, the table + * "borrows" the payload memory, i.e., the user of the table is + * responsible for deallocating the payload memory. See table_mwe2i.c + * for a version where the table takes over the responsibility. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-11: First public version. Adapted from table_mwe1.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_string_int_pair(const void *key, const void *value) +{ + const char *k=key; + const int *v=value; + printf("[%s, %d]\n", k, *v); +} + +// Interpret the supplied key pointer and print its content. +void print_int(const void *key) +{ + const int *k=key; + printf("%d", *k); +} + +// Interpret the supplied value pointer and print its content. +void print_string(const void *value) +{ + const char *v=value; + printf("\\\"%s\\\"", v); +} + +// Compare two keys (char *). +int compare_strings(const void *k1, const void *k2) +{ + const char *key1 = k1; + const char *key2 = k2; + + return strcmp(key1, key2); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const table *t, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + table_print_internal(t, print_string, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Keep track of the key-value pairs we allocate. + char *key[3]; + int *value[3]; + + // Keep the deallocation responsibility of the keys/values we allocate. + table *t = table_empty(compare_strings, NULL, NULL); + + printf("Empty table from the outside:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Empty table showing the inside structure."); + + key[0] = string_copy("Jan"); + value[0] = int_create(31); + table_insert(t, key[0], value[0]); + + key[1] = string_copy("Feb"); + value[1] = int_create(28); + table_insert(t, key[1], value[1]); + + key[2] = string_copy("Mar"); + value[2] = int_create(31); + table_insert(t, key[2], value[2]); + + printf("Table from the outside after adding 3 key-value pairs:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Table with 3 key-value pairs showing the inside structure."); + + // Kill the table, excluding the keys and values we entered. + table_kill(t); + + // Free key/value pairs that we put in the table. + for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) { + string_kill(key[i]); + int_kill(value[i]); + } + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/table/table_mwe2.c b/OU4/src/table/table_mwe2.c new file mode 100644 index 0000000..6dd42eb --- /dev/null +++ b/OU4/src/table/table_mwe2.c @@ -0,0 +1,130 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Makes two lookups and prints the + * result. The responsibility to deallocate the key-value pairs IS + * handed over to the table. Thus, no key-value pointers need to be + * stored outside the table. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.01 2019-03-05: Improved docs. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table with kill_functions.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Delegate the deallocation responsibility to the table. + table *t = table_empty(compare_ints, int_kill, string_kill); + + // We don't need to store the keys and values outside the + // table since the deallocation will be done by the table. + int *key; + char *value; + + key = int_create(90187); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(90184); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(98185); + value = string_copy("Kiruna"); + table_insert(t, key, value); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s=table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + key = int_create(90187); + value = string_copy("Umea (Universitet)"); + table_insert(t, key, value); + + printf("Table after adding a duplicate:\n"); + table_print(t, print_int_string_pair); + + v = 90187; + s = table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Kill the table, including the keys and values we entered. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/table/table_mwe2i.c b/OU4/src/table/table_mwe2i.c new file mode 100644 index 0000000..1ceb2a0 --- /dev/null +++ b/OU4/src/table/table_mwe2i.c @@ -0,0 +1,167 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 4 key-value pairs into + * a table, including one duplicate. Output dot/graphviz code to show + * the internal structure of the table. In this version, the table + * "owns" the payload memory, i.e., the table is responsible for + * deallocating the payload memory. See table_mwe1i.c for a version + * where the table borrows the memory. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2024-04-11: First public version. Adapted from table_mwe1i.c. + */ + +#define VERSION "v1.0" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_string_int_pair(const void *key, const void *value) +{ + const char *k=key; + const int *v=value; + printf("[%s, %d]\n", k, *v); +} + +// Interpret the supplied key pointer and print its content. +void print_int(const void *key) +{ + const int *k=key; + printf("%d", *k); +} + +// Interpret the supplied value pointer and print its content. +void print_string(const void *value) +{ + const char *v=value; + printf("\\\"%s\\\"", v); +} + +// Compare two keys (char *). +int compare_strings(const void *k1, const void *k2) +{ + const char *key1 = k1; + const char *key2 = k2; + + return strcmp(key1, key2); +} + +// Print cut lines before and after a call list_print_internal. +void print_internal_with_cut_lines(const table *t, const char *desc) +{ + // Internal counter that will be remembered between calls. + + // Used to generate sequentially numbered -- CUT HERE -- marker lines + // to enable automatic parsing of the output. + static int cut = 1; + + // Print starting marker line. + printf("\n--- START CUT HERE %d ---\n", cut); + + // Call the internal print function to get the actual dot code. + table_print_internal(t, print_string, print_int, desc, 0); + + // Print ending marker line + printf("--- END CUT HERE %d ---\n\n", cut); + + // Increment the cut number. Will be remembered next time the + // function is called since cut is a static variable. + cut++; +} + +// Print a message with intructions how to use the dot output. prog is +// the name of the executable. +void print_dot_usage(char *prog) +{ + printf("\nGenerate dot code to visualize internal structure with GraphViz. "); + printf("Use\n\n%s ", prog); + printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n"); + printf("to generate an svg file of cut X (replace X by the requested cut number).\n"); + printf("The generated file can then be visualized with\n\n"); + printf("firefox /tmp/dot.svg\n\n"); + printf("Use -Tpng to generate a .png file instead. " + "See graphviz.org and %s for documentation.\n", __FILE__); + + printf("\n--- Start of normal output ---\n\n"); +} + +int main(int argc, char *argv[]) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function.\n", + __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + print_dot_usage(argv[0]); + + // Keep track of the key-value pairs we allocate. + char *key[3]; + int *value[3]; + + // Delegate the deallocation responsibility to the table. + table *t = table_empty(compare_strings, string_kill, int_kill); + + printf("Empty table from the outside:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, "Empty table showing the inside structure."); + + key[0] = string_copy("Jan"); + value[0] = int_create(31); + table_insert(t, key[0], value[0]); + + key[1] = string_copy("Feb"); + value[1] = int_create(28); + table_insert(t, key[1], value[1]); + + key[2] = string_copy("Mar"); + value[2] = int_create(31); + table_insert(t, key[2], value[2]); + + printf("Table from the outside after adding 3 key-value pairs:\n"); + table_print(t, print_string_int_pair); + print_internal_with_cut_lines(t, __FILE__ ": Table with 3 key-value pairs showing the inside structure."); + + // Kill the table, including the keys and values we entered. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/table/table_mwe3.c b/OU4/src/table/table_mwe3.c new file mode 100644 index 0000000..f12d9c1 --- /dev/null +++ b/OU4/src/table/table_mwe3.c @@ -0,0 +1,129 @@ +#include +#include +#include + +#include + +/* + * Minimum working example for table.c. Inserts 3 key-value pairs into + * a table, no duplicates. Makes one lookup and prints the result. The + * responsibility to deallocate the key-value pairs is NOT handed over + * to the table. Uses table_choose_key() to extract keys and values to + * be able to destroy the table without memory leaks or externally + * stored pointers. + * + * Authors: Niclas Borlin (niclas@cs.umu.se) + * Adam Dahlgren Lindstrom (dali@cs.umu.se) + * + * Version information: + * v1.0 2018-02-06: First public version. + * v1.1 2023-01-14: Added printouts at start/end of main. + * v1.2 2024-04-11: Added explicit create/kill of both strings and ints. + */ + +#define VERSION "v1.2" +#define VERSION_DATE "2024-04-11" + +// Create a dynamic copy of the string str. +char* string_copy(const char *str) +{ + // Use calloc to ensure a '\0' termination. + char *copy = calloc(strlen(str) + 1, sizeof(*copy)); + strcpy(copy, str); + return copy; +} + +// Create a dynamic copy of the string str. +void string_kill(void *v) +{ + // Convert pointer - useful in debugging. + char *s = v; + free(s); +} + +// Create a dynamic copy of the integer i. +int *int_create(int i) +{ + // Allocate memory for an integer and set the value + int *v = malloc(sizeof(*v)); + *v = i; + return v; +} + +// Return the memory used by the integer. +void int_kill(void *v) +{ + int *p = v; + free(p); +} + +// Interpret the supplied key and value pointers and print their content. +void print_int_string_pair(const void *key, const void *value) +{ + const int *k=key; + const char *s=value; + printf("[%d, %s]\n", *k, s); +} + +// Compare two keys (int *). +int compare_ints(const void *k1, const void *k2) +{ + int key1 = *(int *)k1; + int key2 = *(int *)k2; + + if ( key1 == key2 ) + return 0; + if ( key1 < key2 ) + return -1; + return 1; +} + +int main(void) +{ + printf("%s, %s %s: Create (integer, string) table without any kill_function " + "nor externally stored pointers, .\n", __FILE__, VERSION, VERSION_DATE); + printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE); + + // Keep the deallocation responsibility. + table *t = table_empty(compare_ints, NULL, NULL); + + int *key; + char *value; + + key = int_create(90187); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(90184); + value = string_copy("Umea"); + table_insert(t, key, value); + + key = int_create(98185); + value = string_copy("Kiruna"); + table_insert(t, key, value); + + printf("Table after inserting 3 pairs:\n"); + table_print(t, print_int_string_pair); + + int v = 90187; + const char *s=table_lookup(t, &v); + printf("Lookup of postal code %d: %s.\n", v, s); + + // Simple cleanup that works if the table is without duplicates. + while (!table_is_empty(t)) { + // Get one key from the table. + key = table_choose_key(t); + // Lookup the corresponding value. + value = table_lookup(t, key); + // Remove the key-value pair from the table. + table_remove(t, key); + // De-allocate key and value. + int_kill(key); + string_kill(value); + } + // Kill what is left by the table. + table_kill(t); + + printf("\nNormal exit.\n\n"); + return 0; +} diff --git a/OU4/src/version/version.c b/OU4/src/version/version.c new file mode 100644 index 0000000..ac91937 --- /dev/null +++ b/OU4/src/version/version.c @@ -0,0 +1,26 @@ +/* + * File to hold library version information for the code base for the + * "Datastructures and algorithms" courses at the Department of + * Computing Science, Umea University. + * + * Author: Niclas Borlin (niclas@cs.umu.se) + * + * Version information: + * v1.0 2023-01-14: First public version. + */ + +#include + +// Create a struct with all version information to be stored in the +// library file. +struct { + const char* long_version; + const char* version; + const unsigned major; + const unsigned minor; + const unsigned revision; + const unsigned patch; +} code_base_version = { + CODE_BASE_LONG_VERSION, CODE_BASE_VERSION, CODE_BASE_MAJOR_VERSION, + CODE_BASE_MINOR_VERSION, CODE_BASE_REVISION, CODE_BASE_PATCH +}; diff --git a/OU4/testbad.map b/OU4/testbad.map new file mode 100644 index 0000000..e69de29 diff --git a/OU4/testmap.map b/OU4/testmap.map new file mode 100644 index 0000000..89ba4f6 --- /dev/null +++ b/OU4/testmap.map @@ -0,0 +1,8 @@ +#testmap handledare gav oss +6 +ARN LLA # Arlanda-Lulea +VXO ARN # Vaxjo-Arlanda +OSD ARN # Ostersund-Arlanda +UME KRN # Umea-Kiruna +KRN OSD # Kiruna-Ostersund +ARN UME # Arlanda-Umea diff --git a/OU4/vgcore.27866 b/OU4/vgcore.27866 new file mode 100644 index 0000000..e9807ac Binary files /dev/null and b/OU4/vgcore.27866 differ diff --git a/OU4/vgcore.28060 b/OU4/vgcore.28060 new file mode 100644 index 0000000..718096e Binary files /dev/null and b/OU4/vgcore.28060 differ diff --git a/OU4/vgcore.28094 b/OU4/vgcore.28094 new file mode 100644 index 0000000..f140e1c Binary files /dev/null and b/OU4/vgcore.28094 differ diff --git a/OU4/vgcore.28162 b/OU4/vgcore.28162 new file mode 100644 index 0000000..7b153ca Binary files /dev/null and b/OU4/vgcore.28162 differ diff --git a/OU4/vgcore.8132 b/OU4/vgcore.8132 new file mode 100644 index 0000000..84fd77d Binary files /dev/null and b/OU4/vgcore.8132 differ diff --git a/OU4/vgcore.8194 b/OU4/vgcore.8194 new file mode 100644 index 0000000..897e423 Binary files /dev/null and b/OU4/vgcore.8194 differ diff --git a/OU4/vgcore.8275 b/OU4/vgcore.8275 new file mode 100644 index 0000000..d8638f5 Binary files /dev/null and b/OU4/vgcore.8275 differ diff --git a/OU4/vgcore.8361 b/OU4/vgcore.8361 new file mode 100644 index 0000000..daa2652 Binary files /dev/null and b/OU4/vgcore.8361 differ diff --git a/OU4/vgcore.8398 b/OU4/vgcore.8398 new file mode 100644 index 0000000..87e6801 Binary files /dev/null and b/OU4/vgcore.8398 differ diff --git a/example/example b/example/example new file mode 100644 index 0000000..4c8fc13 Binary files /dev/null and b/example/example differ diff --git a/example/example.c b/example/example.c new file mode 100644 index 0000000..be264d0 --- /dev/null +++ b/example/example.c @@ -0,0 +1,62 @@ +#include +#include + +// Structure to represent Ellen +typedef struct { + char name[20]; + int age; + char hobby[50]; + char profession[30]; +} Person; + +// Function to introduce Ellen +void introduce_ellen(Person *ellen) { + printf("Hello! My name is %s.\n", ellen->name); + printf("I am %d years old.\n", ellen->age); + printf("I work as a %s.\n", ellen->profession); + printf("In my free time, I enjoy %s.\n", ellen->hobby); +} + +// Function to simulate Ellen's daily activity +void ellen_daily_activity(Person *ellen) { + printf("\n--- %s's Daily Activities ---\n", ellen->name); + printf("Morning: %s starts her day with coffee and planning.\n", ellen->name); + printf("Afternoon: Working hard as a %s.\n", ellen->profession); + printf("Evening: Relaxing with %s.\n", ellen->hobby); +} + +// Function to update Ellen's information +void update_ellen_info(Person *ellen, int new_age, const char *new_hobby) { + ellen->age = new_age; + strcpy(ellen->hobby, new_hobby); + printf("\n%s's information has been updated!\n", ellen->name); +} + +int main() { + // Create Ellen + Person ellen; + strcpy(ellen.name, "Ellen"); + ellen.age = 28; + strcpy(ellen.profession, "Software Developer"); + strcpy(ellen.hobby, "reading books"); + + printf("=== Welcome to Ellen's Program! ===\n\n"); + + // Introduce Ellen + introduce_ellen(&ellen); + + // Show Ellen's daily activities + ellen_daily_activity(&ellen); + + // Update Ellen's information + printf("\n--- One year later ---\n"); + update_ellen_info(&ellen, 29, "hiking and photography"); + + // Show updated information + printf("\nUpdated information:\n"); + introduce_ellen(&ellen); + + printf("\nThanks for learning about Ellen! 🌟\n"); + + return 0; +} \ No newline at end of file