From 96ff5392c855b762415449824ac7fea6fadf0555 Mon Sep 17 00:00:00 2001 From: Maximilian Eibl Date: Wed, 9 Apr 2025 10:22:44 +0200 Subject: [PATCH] init --- exercise1/.clang-format | 11 + exercise1/.clangd | 20 + exercise1/.ctests | 5 + exercise1/.expected-files | 5 + exercise1/.gitattributes | 16 + exercise1/.gitignore | 360 +++ exercise1/.gitmodules | 4 + exercise1/CMakeLists.txt | 59 + exercise1/README.md | 19 + exercise1/compile_flags.txt | 1 + exercise1/info_python.txt | 4 + exercise1/main.ipynb | 204 ++ exercise1/modules | 1 + exercise1/task1.test.c | 27 + exercise1/task1.test.cpp | 35 + exercise1/task1.test.py | 49 + exercise1/task2.cpp | 25 + exercise1/task3.cpp | 65 + exercise1/task3.hpp | 49 + exercise1/task3.test.cpp | 44 + exercise10/.clang-format | 10 + exercise10/.clangd | 25 + exercise10/.ctests | 3 + exercise10/.expected-files | 3 + exercise10/.gitattributes | 16 + exercise10/.gitignore | 358 +++ exercise10/.gitmodules | 4 + exercise10/CMakeLists.txt | 71 + exercise10/README.md | 19 + exercise10/compile_flags.txt | 1 + exercise10/invalid.csv | 5 + exercise10/main.ipynb | 203 ++ exercise10/matrix4x2.csv | 5 + exercise10/matrix4x2_rcols.csv | 4 + exercise10/matrix4x2_rrow.csv | 4 + exercise10/modules | 1 + exercise10/rcol2x2.csv | 3 + exercise10/rrev4x4.csv | 5 + exercise10/task1.main.c | 67 + exercise10/task2.c | 102 + exercise10/task2.h | 51 + exercise10/task2.test.c | 220 ++ exercise10/task3.main.c | 196 ++ exercise10/task3.test.c | 88 + exercise2/.clang-format | 11 + exercise2/.clangd | 25 + exercise2/.ctests | 3 + exercise2/.expected-files | 3 + exercise2/.gitattributes | 16 + exercise2/.gitignore | 358 +++ exercise2/CMakeLists.txt | 42 + exercise2/README.md | 20 + exercise2/compile_flags.txt | 1 + exercise2/gitblah-sHB2 | 2 + exercise2/main.ipynb | 192 ++ exercise2/task1.main.cpp | 33 + exercise2/task2.cpp | 117 + exercise2/task2.hpp | 55 + exercise2/task2.test.cpp | 112 + exercise2/task3.cpp | 33 + exercise2/task3.hpp | 20 + exercise2/task3.test.cpp | 54 + exercise3/.clang-format | 10 + exercise3/.clangd | 25 + exercise3/.ctests | 4 + exercise3/.expected-files | 4 + exercise3/.gitattributes | 16 + exercise3/.gitignore | 360 +++ exercise3/.gitmodules | 4 + exercise3/CMakeLists.txt | 54 + exercise3/README.md | 29 + exercise3/compile_flags.txt | 1 + exercise3/main.ipynb | 248 ++ exercise3/modules | 1 + exercise3/task1.main.cpp | 38 + exercise3/task2.cpp | 71 + exercise3/task2.hpp | 34 + exercise3/task2.test.cpp | 97 + exercise3/task3.cpp | 45 + exercise3/task3.hpp | 22 + exercise3/task3.py | 50 + exercise3/task3.test.cpp | 59 + exercise3/task3.test.py | 39 + exercise4/.clang-format | 10 + exercise4/.clangd | 25 + exercise4/.ctests | 3 + exercise4/.expected-files | 3 + exercise4/.gitattributes | 16 + exercise4/.gitignore | 360 +++ exercise4/.gitmodules | 4 + exercise4/CMakeLists.txt | 50 + exercise4/README.md | 19 + exercise4/compile_flags.txt | 1 + exercise4/images/dusty_nikolaus_city.svg | 1145 ++++++++ exercise4/images/trains.svg | 265 ++ exercise4/main.ipynb | 256 ++ exercise4/modules | 1 + exercise4/task1.main.cpp | 43 + exercise4/task2.cpp | 171 ++ exercise4/task2.hpp | 87 + exercise4/task2.misc.cpp | 86 + exercise4/task2.misc.hpp | 30 + exercise4/task2.test.cpp | 133 + exercise4/task3.cpp | 55 + exercise4/task3.hpp | 16 + exercise4/task3.misc.cpp | 34 + exercise4/task3.misc.hpp | 25 + exercise4/task3.test.cpp | 22 + exercise5/.clang-format | 10 + exercise5/.clangd | 25 + exercise5/.ctests | 3 + exercise5/.expected-files | 3 + exercise5/.gitattributes | 16 + exercise5/.gitignore | 360 +++ exercise5/.gitmodules | 4 + exercise5/CMakeLists.txt | 47 + exercise5/README.md | 19 + exercise5/TriangleMesh.cpp | 217 ++ exercise5/TriangleMesh.hpp | 74 + exercise5/compile_flags.txt | 1 + exercise5/images/annuluses1.svg | 2500 +++++++++++++++++ exercise5/images/annuluses2.svg | 2467 ++++++++++++++++ exercise5/images/annuluses3.svg | 3241 ++++++++++++++++++++++ exercise5/main.ipynb | 242 ++ exercise5/modules | 1 + exercise5/task1.main.cpp | 35 + exercise5/task2.cpp | 60 + exercise5/task2.hpp | 42 + exercise5/task2.test.cpp | 86 + exercise5/task3.cpp | 78 + exercise5/task3.hpp | 21 + exercise5/task3.test.cpp | 37 + exercise6/.clang-format | 10 + exercise6/.clangd | 25 + exercise6/.ctests | 3 + exercise6/.expected-files | 4 + exercise6/.gitattributes | 16 + exercise6/.gitignore | 360 +++ exercise6/CMakeLists.txt | 47 + exercise6/README.md | 18 + exercise6/compile_flags.txt | 1 + exercise6/images/task3_output.svg | 10 + exercise6/main.ipynb | 228 ++ exercise6/task1.hierarchy.hpp | 27 + exercise6/task1.main.cpp | 57 + exercise6/task2.Circle.hpp | 43 + exercise6/task2.Element.hpp | 25 + exercise6/task2.Line.hpp | 42 + exercise6/task2.Polyline.hpp | 48 + exercise6/task2.aliases.hpp | 18 + exercise6/task2.test.cpp | 69 + exercise6/task3.Image.hpp | 63 + exercise6/task3.ImageUP.hpp | 53 + exercise6/task3.Root.hpp | 55 + exercise6/task3.cpp | 33 + exercise6/task3.hpp | 22 + exercise6/task3.test.cpp | 45 + exercise7/.clang-format | 10 + exercise7/.clangd | 25 + exercise7/.ctests | 3 + exercise7/.expected-files | 3 + exercise7/.gitattributes | 16 + exercise7/.gitignore | 358 +++ exercise7/CMakeLists.txt | 47 + exercise7/README.md | 18 + exercise7/compile_flags.txt | 1 + exercise7/main.ipynb | 193 ++ exercise7/task1.main.cpp | 25 + exercise7/task2.hpp | 81 + exercise7/task2.test.cpp | 88 + exercise7/task3.concept.hpp | 24 + exercise7/task3.hpp | 10 + exercise7/task3.test.cpp | 17 + exercise8/.clang-format | 10 + exercise8/.clangd | 25 + exercise8/.ctests | 3 + exercise8/.expected-files | 3 + exercise8/.gitattributes | 16 + exercise8/.gitignore | 358 +++ exercise8/.gitmodules | 4 + exercise8/CMakeLists.txt | 59 + exercise8/README.md | 19 + exercise8/compile_flags.txt | 1 + exercise8/main.ipynb | 187 ++ exercise8/modules | 1 + exercise8/task1.main.c | 44 + exercise8/task2.c | 52 + exercise8/task2.h | 51 + exercise8/task2.test.c | 220 ++ exercise8/task3.c | 54 + exercise8/task3.h | 19 + exercise8/task3.test.c | 68 + exercise9/.idea/.gitignore | 8 + exercise9/.idea/exercise9.iml | 8 + exercise9/.idea/modules.xml | 8 + exercise9/.idea/vcs.xml | 6 + exercise9/README.md | 19 + exercise9/main.ipynb | 217 ++ exercise9/task1.main.c | 18 + exercise9/task2.c | 22 + exercise9/task2.h | 60 + exercise9/task2.test.c | 126 + exercise9/task3.c | 12 + exercise9/task3.h | 25 + exercise9/task3.test.c | 62 + lab0/.gitattributes | 16 + lab0/.gitignore | 358 +++ lab0/README.md | 138 + lab1/.clang-format | 11 + lab1/.clangd | 23 + lab1/.gitattributes | 16 + lab1/.gitignore | 358 +++ lab1/.gitmodules | 4 + lab1/CMakeLists.txt | 46 + lab1/README.md | 25 + lab1/compile_flags.txt | 1 + lab1/main.ipynb | 160 ++ lab1/task.cpp | 14 + lab1/taskA.cpp | 26 + lab1/taskB.cpp | 95 + lab1/taskC.cpp | 43 + lab2/.clang-format | 11 + lab2/.clangd | 23 + lab2/.gitattributes | 16 + lab2/.gitignore | 358 +++ lab2/.gitmodules | 4 + lab2/CMakeLists.txt | 26 + lab2/README.md | 25 + lab2/compile_flags.txt | 1 + lab2/images/01.jpg | Bin 0 -> 53838 bytes lab2/images/02.jpg | Bin 0 -> 78895 bytes lab2/images/03.jpg | Bin 0 -> 61586 bytes lab2/images/04.jpg | Bin 0 -> 69814 bytes lab2/images/05.jpg | Bin 0 -> 62013 bytes lab2/images/06.jpg | Bin 0 -> 91139 bytes lab2/last_task.cpp | 25 + lab2/main.ipynb | 144 + lab2/task.cpp | 29 + lab2/taskA.cpp | 62 + lab2/taskB.cpp | 76 + lab2/taskC.cpp | 44 + lab3/.clang-format | 11 + lab3/.clangd | 23 + lab3/.gitattributes | 16 + lab3/.gitignore | 358 +++ lab3/.gitmodules | 4 + lab3/CMakeLists.txt | 30 + lab3/README.md | 25 + lab3/compile_flags.txt | 1 + lab3/main.ipynb | 112 + lab3/modules/.clang-format | 11 + lab3/modules/.clangd | 20 + lab3/modules/.gitattributes | 16 + lab3/modules/.gitignore | 358 +++ lab3/modules/CMakeLists.txt | 62 + lab3/modules/CMakePresets.json | 30 + lab3/modules/README.md | 94 + lab3/modules/cmake/iue-modules-config.in | 13 + lab3/modules/compile_flags.txt | 1 + lab3/modules/iue-io/CMakeLists.txt | 52 + lab3/modules/iue-io/ccsv.h | 143 + lab3/modules/iue-io/ccsv.test.c | 63 + lab3/modules/iue-io/csv.hpp | 99 + lab3/modules/iue-io/csv.test.cpp | 73 + lab3/modules/iue-num/CMakeLists.txt | 30 + lab3/modules/iue-num/numerics.hpp | 26 + lab3/modules/iue-num/numerics.test.cpp | 69 + lab3/modules/iue-other/CMakeLists.txt | 53 + lab3/modules/iue-other/func.detail.hpp | 7 + lab3/modules/iue-other/func.hpp | 9 + lab3/modules/iue-other/func.test.cpp | 9 + lab3/modules/iue-other/library.cpp | 9 + lab3/modules/iue-other/library.hpp | 11 + lab3/modules/iue-other/library.test.cpp | 12 + lab3/modules/iue-po/CMakeLists.txt | 51 + lab3/modules/iue-po/cpo.h | 104 + lab3/modules/iue-po/cpo.test.c | 32 + lab3/modules/iue-po/po.hpp | 60 + lab3/modules/iue-po/po.test.cpp | 23 + lab3/modules/iue-rnd/CMakeLists.txt | 30 + lab3/modules/iue-rnd/random.hpp | 128 + lab3/modules/iue-rnd/random.test.cpp | 93 + lab3/modules/iue-svg/CMakeLists.txt | 39 + lab3/modules/iue-svg/render.hpp | 157 ++ lab3/modules/iue-svg/render.test.cpp | 37 + lab3/modules/iue-svg/tags.hpp | 151 + lab3/modules/iue-svg/tags.test.cpp | 62 + lab3/modules/iue-svg/tree.hpp | 56 + lab3/modules/iue-svg/tree.test.cpp | 60 + lab3/taskA.Vector.hpp | 47 + lab3/taskA.VectorT.hpp | 37 + lab3/taskA.cpp | 34 + lab3/taskB.Car.hpp | 26 + lab3/taskB.Device.hpp | 13 + lab3/taskB.Train.hpp | 25 + lab3/taskB.WashingMachine.hpp | 25 + lab3/taskB.cpp | 35 + lab3/taskC.cpp | 27 + lab3/taskC.one.hpp | 31 + lab3/taskC.two.hpp | 27 + lab5/.clang-format | 11 + lab5/.clangd | 23 + lab5/.gitattributes | 16 + lab5/.gitignore | 364 +++ lab5/.gitmodules | 4 + lab5/CMakeLists.txt | 40 + lab5/README.md | 25 + lab5/compile_flags.txt | 1 + lab5/main.ipynb | 111 + lab5/table1.csv | 3 + lab5/table1m.csv | 3 + lab5/table2.csv | 3 + lab5/task.c | 24 + lab5/taskA.c | 46 + lab5/taskB.c | 74 + lab5/taskC.c | 87 + lab6/.clang-format | 11 + lab6/.clangd | 23 + lab6/.gitattributes | 16 + lab6/.gitignore | 364 +++ lab6/.gitmodules | 8 + lab6/CMakeLists.txt | 38 + lab6/README.md | 24 + lab6/compile_flags.txt | 2 + lab6/images/benchmark_lin.png | Bin 0 -> 38162 bytes lab6/images/benchmark_logy.png | Bin 0 -> 40088 bytes lab6/main.ipynb | 94 + lab6/modules | 1 + lab6/taskB.cpp | 101 + lab6/taskB.py | 95 + 330 files changed, 28300 insertions(+) create mode 100644 exercise1/.clang-format create mode 100644 exercise1/.clangd create mode 100644 exercise1/.ctests create mode 100644 exercise1/.expected-files create mode 100644 exercise1/.gitattributes create mode 100644 exercise1/.gitignore create mode 100644 exercise1/.gitmodules create mode 100644 exercise1/CMakeLists.txt create mode 100644 exercise1/README.md create mode 100644 exercise1/compile_flags.txt create mode 100644 exercise1/info_python.txt create mode 100644 exercise1/main.ipynb create mode 160000 exercise1/modules create mode 100644 exercise1/task1.test.c create mode 100644 exercise1/task1.test.cpp create mode 100644 exercise1/task1.test.py create mode 100644 exercise1/task2.cpp create mode 100644 exercise1/task3.cpp create mode 100644 exercise1/task3.hpp create mode 100644 exercise1/task3.test.cpp create mode 100644 exercise10/.clang-format create mode 100644 exercise10/.clangd create mode 100644 exercise10/.ctests create mode 100644 exercise10/.expected-files create mode 100644 exercise10/.gitattributes create mode 100644 exercise10/.gitignore create mode 100644 exercise10/.gitmodules create mode 100644 exercise10/CMakeLists.txt create mode 100644 exercise10/README.md create mode 100644 exercise10/compile_flags.txt create mode 100644 exercise10/invalid.csv create mode 100644 exercise10/main.ipynb create mode 100644 exercise10/matrix4x2.csv create mode 100644 exercise10/matrix4x2_rcols.csv create mode 100644 exercise10/matrix4x2_rrow.csv create mode 160000 exercise10/modules create mode 100644 exercise10/rcol2x2.csv create mode 100644 exercise10/rrev4x4.csv create mode 100644 exercise10/task1.main.c create mode 100644 exercise10/task2.c create mode 100644 exercise10/task2.h create mode 100644 exercise10/task2.test.c create mode 100644 exercise10/task3.main.c create mode 100644 exercise10/task3.test.c create mode 100644 exercise2/.clang-format create mode 100644 exercise2/.clangd create mode 100644 exercise2/.ctests create mode 100644 exercise2/.expected-files create mode 100644 exercise2/.gitattributes create mode 100644 exercise2/.gitignore create mode 100644 exercise2/CMakeLists.txt create mode 100644 exercise2/README.md create mode 100644 exercise2/compile_flags.txt create mode 100644 exercise2/gitblah-sHB2 create mode 100644 exercise2/main.ipynb create mode 100644 exercise2/task1.main.cpp create mode 100644 exercise2/task2.cpp create mode 100644 exercise2/task2.hpp create mode 100644 exercise2/task2.test.cpp create mode 100644 exercise2/task3.cpp create mode 100644 exercise2/task3.hpp create mode 100644 exercise2/task3.test.cpp create mode 100644 exercise3/.clang-format create mode 100644 exercise3/.clangd create mode 100644 exercise3/.ctests create mode 100644 exercise3/.expected-files create mode 100644 exercise3/.gitattributes create mode 100644 exercise3/.gitignore create mode 100644 exercise3/.gitmodules create mode 100644 exercise3/CMakeLists.txt create mode 100644 exercise3/README.md create mode 100644 exercise3/compile_flags.txt create mode 100644 exercise3/main.ipynb create mode 160000 exercise3/modules create mode 100644 exercise3/task1.main.cpp create mode 100644 exercise3/task2.cpp create mode 100644 exercise3/task2.hpp create mode 100644 exercise3/task2.test.cpp create mode 100644 exercise3/task3.cpp create mode 100644 exercise3/task3.hpp create mode 100644 exercise3/task3.py create mode 100644 exercise3/task3.test.cpp create mode 100644 exercise3/task3.test.py create mode 100644 exercise4/.clang-format create mode 100644 exercise4/.clangd create mode 100644 exercise4/.ctests create mode 100644 exercise4/.expected-files create mode 100644 exercise4/.gitattributes create mode 100644 exercise4/.gitignore create mode 100644 exercise4/.gitmodules create mode 100644 exercise4/CMakeLists.txt create mode 100644 exercise4/README.md create mode 100644 exercise4/compile_flags.txt create mode 100644 exercise4/images/dusty_nikolaus_city.svg create mode 100644 exercise4/images/trains.svg create mode 100644 exercise4/main.ipynb create mode 160000 exercise4/modules create mode 100644 exercise4/task1.main.cpp create mode 100644 exercise4/task2.cpp create mode 100644 exercise4/task2.hpp create mode 100644 exercise4/task2.misc.cpp create mode 100644 exercise4/task2.misc.hpp create mode 100644 exercise4/task2.test.cpp create mode 100644 exercise4/task3.cpp create mode 100644 exercise4/task3.hpp create mode 100644 exercise4/task3.misc.cpp create mode 100644 exercise4/task3.misc.hpp create mode 100644 exercise4/task3.test.cpp create mode 100644 exercise5/.clang-format create mode 100644 exercise5/.clangd create mode 100644 exercise5/.ctests create mode 100644 exercise5/.expected-files create mode 100644 exercise5/.gitattributes create mode 100644 exercise5/.gitignore create mode 100644 exercise5/.gitmodules create mode 100644 exercise5/CMakeLists.txt create mode 100644 exercise5/README.md create mode 100644 exercise5/TriangleMesh.cpp create mode 100644 exercise5/TriangleMesh.hpp create mode 100644 exercise5/compile_flags.txt create mode 100644 exercise5/images/annuluses1.svg create mode 100644 exercise5/images/annuluses2.svg create mode 100644 exercise5/images/annuluses3.svg create mode 100644 exercise5/main.ipynb create mode 160000 exercise5/modules create mode 100644 exercise5/task1.main.cpp create mode 100644 exercise5/task2.cpp create mode 100644 exercise5/task2.hpp create mode 100644 exercise5/task2.test.cpp create mode 100644 exercise5/task3.cpp create mode 100644 exercise5/task3.hpp create mode 100644 exercise5/task3.test.cpp create mode 100644 exercise6/.clang-format create mode 100644 exercise6/.clangd create mode 100644 exercise6/.ctests create mode 100644 exercise6/.expected-files create mode 100644 exercise6/.gitattributes create mode 100644 exercise6/.gitignore create mode 100644 exercise6/CMakeLists.txt create mode 100644 exercise6/README.md create mode 100644 exercise6/compile_flags.txt create mode 100644 exercise6/images/task3_output.svg create mode 100644 exercise6/main.ipynb create mode 100644 exercise6/task1.hierarchy.hpp create mode 100644 exercise6/task1.main.cpp create mode 100644 exercise6/task2.Circle.hpp create mode 100644 exercise6/task2.Element.hpp create mode 100644 exercise6/task2.Line.hpp create mode 100644 exercise6/task2.Polyline.hpp create mode 100644 exercise6/task2.aliases.hpp create mode 100644 exercise6/task2.test.cpp create mode 100644 exercise6/task3.Image.hpp create mode 100644 exercise6/task3.ImageUP.hpp create mode 100644 exercise6/task3.Root.hpp create mode 100644 exercise6/task3.cpp create mode 100644 exercise6/task3.hpp create mode 100644 exercise6/task3.test.cpp create mode 100644 exercise7/.clang-format create mode 100644 exercise7/.clangd create mode 100644 exercise7/.ctests create mode 100644 exercise7/.expected-files create mode 100644 exercise7/.gitattributes create mode 100644 exercise7/.gitignore create mode 100644 exercise7/CMakeLists.txt create mode 100644 exercise7/README.md create mode 100644 exercise7/compile_flags.txt create mode 100644 exercise7/main.ipynb create mode 100644 exercise7/task1.main.cpp create mode 100644 exercise7/task2.hpp create mode 100644 exercise7/task2.test.cpp create mode 100644 exercise7/task3.concept.hpp create mode 100644 exercise7/task3.hpp create mode 100644 exercise7/task3.test.cpp create mode 100644 exercise8/.clang-format create mode 100644 exercise8/.clangd create mode 100644 exercise8/.ctests create mode 100644 exercise8/.expected-files create mode 100644 exercise8/.gitattributes create mode 100644 exercise8/.gitignore create mode 100644 exercise8/.gitmodules create mode 100644 exercise8/CMakeLists.txt create mode 100644 exercise8/README.md create mode 100644 exercise8/compile_flags.txt create mode 100644 exercise8/main.ipynb create mode 160000 exercise8/modules create mode 100644 exercise8/task1.main.c create mode 100644 exercise8/task2.c create mode 100644 exercise8/task2.h create mode 100644 exercise8/task2.test.c create mode 100644 exercise8/task3.c create mode 100644 exercise8/task3.h create mode 100644 exercise8/task3.test.c create mode 100644 exercise9/.idea/.gitignore create mode 100644 exercise9/.idea/exercise9.iml create mode 100644 exercise9/.idea/modules.xml create mode 100644 exercise9/.idea/vcs.xml create mode 100644 exercise9/README.md create mode 100644 exercise9/main.ipynb create mode 100644 exercise9/task1.main.c create mode 100644 exercise9/task2.c create mode 100644 exercise9/task2.h create mode 100644 exercise9/task2.test.c create mode 100644 exercise9/task3.c create mode 100644 exercise9/task3.h create mode 100644 exercise9/task3.test.c create mode 100644 lab0/.gitattributes create mode 100644 lab0/.gitignore create mode 100644 lab0/README.md create mode 100644 lab1/.clang-format create mode 100644 lab1/.clangd create mode 100644 lab1/.gitattributes create mode 100644 lab1/.gitignore create mode 100644 lab1/.gitmodules create mode 100644 lab1/CMakeLists.txt create mode 100644 lab1/README.md create mode 100644 lab1/compile_flags.txt create mode 100644 lab1/main.ipynb create mode 100644 lab1/task.cpp create mode 100644 lab1/taskA.cpp create mode 100644 lab1/taskB.cpp create mode 100644 lab1/taskC.cpp create mode 100644 lab2/.clang-format create mode 100644 lab2/.clangd create mode 100644 lab2/.gitattributes create mode 100644 lab2/.gitignore create mode 100644 lab2/.gitmodules create mode 100644 lab2/CMakeLists.txt create mode 100644 lab2/README.md create mode 100644 lab2/compile_flags.txt create mode 100644 lab2/images/01.jpg create mode 100644 lab2/images/02.jpg create mode 100644 lab2/images/03.jpg create mode 100644 lab2/images/04.jpg create mode 100644 lab2/images/05.jpg create mode 100644 lab2/images/06.jpg create mode 100644 lab2/last_task.cpp create mode 100644 lab2/main.ipynb create mode 100644 lab2/task.cpp create mode 100644 lab2/taskA.cpp create mode 100644 lab2/taskB.cpp create mode 100644 lab2/taskC.cpp create mode 100644 lab3/.clang-format create mode 100644 lab3/.clangd create mode 100644 lab3/.gitattributes create mode 100644 lab3/.gitignore create mode 100644 lab3/.gitmodules create mode 100644 lab3/CMakeLists.txt create mode 100644 lab3/README.md create mode 100644 lab3/compile_flags.txt create mode 100644 lab3/main.ipynb create mode 100644 lab3/modules/.clang-format create mode 100644 lab3/modules/.clangd create mode 100644 lab3/modules/.gitattributes create mode 100644 lab3/modules/.gitignore create mode 100644 lab3/modules/CMakeLists.txt create mode 100644 lab3/modules/CMakePresets.json create mode 100644 lab3/modules/README.md create mode 100644 lab3/modules/cmake/iue-modules-config.in create mode 100644 lab3/modules/compile_flags.txt create mode 100644 lab3/modules/iue-io/CMakeLists.txt create mode 100644 lab3/modules/iue-io/ccsv.h create mode 100644 lab3/modules/iue-io/ccsv.test.c create mode 100644 lab3/modules/iue-io/csv.hpp create mode 100644 lab3/modules/iue-io/csv.test.cpp create mode 100644 lab3/modules/iue-num/CMakeLists.txt create mode 100644 lab3/modules/iue-num/numerics.hpp create mode 100644 lab3/modules/iue-num/numerics.test.cpp create mode 100644 lab3/modules/iue-other/CMakeLists.txt create mode 100644 lab3/modules/iue-other/func.detail.hpp create mode 100644 lab3/modules/iue-other/func.hpp create mode 100644 lab3/modules/iue-other/func.test.cpp create mode 100644 lab3/modules/iue-other/library.cpp create mode 100644 lab3/modules/iue-other/library.hpp create mode 100644 lab3/modules/iue-other/library.test.cpp create mode 100644 lab3/modules/iue-po/CMakeLists.txt create mode 100644 lab3/modules/iue-po/cpo.h create mode 100644 lab3/modules/iue-po/cpo.test.c create mode 100644 lab3/modules/iue-po/po.hpp create mode 100644 lab3/modules/iue-po/po.test.cpp create mode 100644 lab3/modules/iue-rnd/CMakeLists.txt create mode 100644 lab3/modules/iue-rnd/random.hpp create mode 100644 lab3/modules/iue-rnd/random.test.cpp create mode 100644 lab3/modules/iue-svg/CMakeLists.txt create mode 100644 lab3/modules/iue-svg/render.hpp create mode 100644 lab3/modules/iue-svg/render.test.cpp create mode 100644 lab3/modules/iue-svg/tags.hpp create mode 100644 lab3/modules/iue-svg/tags.test.cpp create mode 100644 lab3/modules/iue-svg/tree.hpp create mode 100644 lab3/modules/iue-svg/tree.test.cpp create mode 100644 lab3/taskA.Vector.hpp create mode 100644 lab3/taskA.VectorT.hpp create mode 100644 lab3/taskA.cpp create mode 100644 lab3/taskB.Car.hpp create mode 100644 lab3/taskB.Device.hpp create mode 100644 lab3/taskB.Train.hpp create mode 100644 lab3/taskB.WashingMachine.hpp create mode 100644 lab3/taskB.cpp create mode 100644 lab3/taskC.cpp create mode 100644 lab3/taskC.one.hpp create mode 100644 lab3/taskC.two.hpp create mode 100644 lab5/.clang-format create mode 100644 lab5/.clangd create mode 100644 lab5/.gitattributes create mode 100644 lab5/.gitignore create mode 100644 lab5/.gitmodules create mode 100644 lab5/CMakeLists.txt create mode 100644 lab5/README.md create mode 100644 lab5/compile_flags.txt create mode 100644 lab5/main.ipynb create mode 100644 lab5/table1.csv create mode 100644 lab5/table1m.csv create mode 100644 lab5/table2.csv create mode 100644 lab5/task.c create mode 100644 lab5/taskA.c create mode 100644 lab5/taskB.c create mode 100644 lab5/taskC.c create mode 100644 lab6/.clang-format create mode 100644 lab6/.clangd create mode 100644 lab6/.gitattributes create mode 100644 lab6/.gitignore create mode 100644 lab6/.gitmodules create mode 100644 lab6/CMakeLists.txt create mode 100644 lab6/README.md create mode 100644 lab6/compile_flags.txt create mode 100644 lab6/images/benchmark_lin.png create mode 100644 lab6/images/benchmark_logy.png create mode 100644 lab6/main.ipynb create mode 160000 lab6/modules create mode 100644 lab6/taskB.cpp create mode 100644 lab6/taskB.py diff --git a/exercise1/.clang-format b/exercise1/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/exercise1/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise1/.clangd b/exercise1/.clangd new file mode 100644 index 0000000..6396dd7 --- /dev/null +++ b/exercise1/.clangd @@ -0,0 +1,20 @@ +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/exercise1/.ctests b/exercise1/.ctests new file mode 100644 index 0000000..a5c4061 --- /dev/null +++ b/exercise1/.ctests @@ -0,0 +1,5 @@ +task1_py +task1_c +task1_cpp +task2 +task3 diff --git a/exercise1/.expected-files b/exercise1/.expected-files new file mode 100644 index 0000000..6d5c620 --- /dev/null +++ b/exercise1/.expected-files @@ -0,0 +1,5 @@ +info_python.txt +info_c.txt +info_cpp.txt +task2.main.cpp +task3.cpp diff --git a/exercise1/.gitattributes b/exercise1/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise1/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise1/.gitignore b/exercise1/.gitignore new file mode 100644 index 0000000..efff150 --- /dev/null +++ b/exercise1/.gitignore @@ -0,0 +1,360 @@ +# custom + +*.csv +*.png +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/exercise1/.gitmodules b/exercise1/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/exercise1/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/exercise1/CMakeLists.txt b/exercise1/CMakeLists.txt new file mode 100644 index 0000000..b80d4c1 --- /dev/null +++ b/exercise1/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise3 LANGUAGES CXX C + DESCRIPTION "exercise1" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise1") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED True) + set(CMAKE_C_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + find_package(Python3 COMPONENTS Interpreter REQUIRED) + add_test(NAME task1_py COMMAND ${Python3_EXECUTABLE} task1.test.py WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task1_c task1.test.c) + add_test(NAME task1_c COMMAND task1_c WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task1_cpp task1.test.cpp) + add_test(NAME task1_cpp COMMAND task1_cpp WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_custom_target(task1) + add_dependencies(task1 task1_c task1_cpp) + + add_executable(task2 task2.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task2 PROPERTY PROPERTY_REGULAR_EXPRESSION "6633") + + add_executable(task3 task3.cpp task3.test.cpp) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) diff --git a/exercise1/README.md b/exercise1/README.md new file mode 100644 index 0000000..80d06a9 --- /dev/null +++ b/exercise1/README.md @@ -0,0 +1,19 @@ +# Hausübung 1 (3 Punkte) + +**Ausgabe**: Donnerstag 7. März 2024, vormittags. + +**Abgabe bis**: Montag 18. März 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise1`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Konfiguration der benötigten Werkzeuge +- Implementierung/Kompilierung/Ausführen eines lauffähigen Programms +- Einfache Funktionen (ohne Bedingungen/Verzweigungen) mit Fundamentalen Typen als Parameter und Rückgabewert + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise1/compile_flags.txt b/exercise1/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise1/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise1/info_python.txt b/exercise1/info_python.txt new file mode 100644 index 0000000..7d37375 --- /dev/null +++ b/exercise1/info_python.txt @@ -0,0 +1,4 @@ +os: Windows 11 10.0.22631 +py: CPython 3.12.2 +np: numpy 1.26.4 +mpl: matplotlib 3.8.3 diff --git a/exercise1/main.ipynb b/exercise1/main.ipynb new file mode 100644 index 0000000..de5e4bf --- /dev/null +++ b/exercise1/main.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Konfigurieren und Testen der eigenen Umgebung (1 Punkt)\n", + "\n", + "1. Konfigurieren Sie Ihr eigenes System: https://sgit.iue.tuwien.ac.at/360050/setup\n", + "\n", + "2. Nachdem Sie Ihre Konfiguration abgeschlossen haben, testen Sie Ihre Konfiguration indem Sie folgenden drei mitgelieferte Tests ausführen:\n", + "\n", + "\t- [task1.test.py](task1.test.py) testet die Python-Konfiguration. \n", + "\t- [task1.test.c](task1.test.c) testet die C-Konfiguration.\n", + "\t- [task1.test.cpp](task1.test.cpp) testet die C++-Konfiguration." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Kompilieren/Ausführen \n", + "\n", + "Mittels manueller Aufrufe (`gcc/g++/python`):\n", + "```shell\n", + "# prepare folder\n", + "mkdir build\n", + "# compile for task1\n", + "gcc -Imodules -std=c11 -g task1.test.c -o build/task1_c.exe\n", + "g++ -Imodules -std=c++20 -g task1.test.cpp -o build/task1_cpp.exe\n", + "# run tests for task1\n", + "python task1.test.py # produces \"info_python.txt\"\n", + "./build/task1_c.exe # produces \"info_c.txt\"\n", + "./build/task1_cpp.exe # produces \"info_cpp.txt\"\n", + "```\n", + "\n", + "Alternativ mittels *CMake* (optional):\n", + "```shell\n", + "# prepare compilation\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile for task1\n", + "cmake --build build --config Debug --target task1\n", + "# run tests for task1\n", + "ctest --test-dir build -C Debug -R task1\n", + "```\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Ein eigenes kleines C++-Programm (1 Punkt)\n", + "\n", + "Erstellen Sie ein lauffähiges *Ein-Dateien-Programm* das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::cout|endl\n", + "\t#include <...>\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion, z.B.:\n", + "\t```cpp\n", + "\tint sum(...){\n", + "\t ...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion (Einstiegspunkt für jedes lauffähige Programm), die Ihre selbest geschriebene Funktion verwendet und die berechneten Ergebnisse in der Konsole ausgibt, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\n", + "\t auto res = sum(...)\t\n", + "\t std::cout << res << std::endl;\n", + "\t return 0;\n", + "\t}\n", + "\t``` " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [task2.main.cpp](task2.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [task2.main.cpp](task2.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Kompilieren/Ausführen \n", + "\n", + "Mittels manueller Aufrufe (`gcc/g++/python`):\n", + "```shell\n", + "# prepare folder\n", + "mkdir build\n", + "# compile for task2\n", + "g++ -std=c++20 -g task2.main.cpp -o build/task2.exe\n", + "# run test (your output will be parsed when graded)\n", + "./build/task2.exe\n", + "```\n", + "\n", + "Alternativ mittels *CMake* (optional):\n", + "```shell\n", + "# prepare compilation\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile for task2\n", + "cmake --build build --config Debug --target task2\n", + "# run tests for task2\n", + "ctest --test-dir build -C Debug -R task2\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Einfache Funktionen ohne Verzweigungen (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie folgende Funktionen:\n", + "\n", + "```cpp\n", + "double add(double x, double y, double z);\n", + "double mul(double x, double y, double z);\n", + "double frac(double x, double y);\n", + "double mean(double x, double y, double z);\n", + "double squared(double x);\n", + "double cubed(double x);\n", + "double eval(double x, double a, double b, double c);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklarationen und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Kompilieren/Ausführen \n", + "\n", + "Mittels manueller Aufrufe (`gcc/g++/python`):\n", + "```shell\n", + "# prepare folder\n", + "mkdir build\n", + "# compile for task3\n", + "g++ -std=c++20 -g task3.cpp task3.test.cpp -o build/task3.exe\n", + "# run test\n", + "./build/task3.exe\n", + "```\n", + "\n", + "Alternativ mittels *CMake* (optional):\n", + "```shell\n", + "# prepare compilation\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile for task3\n", + "cmake --build build --config Debug --target task3\n", + "# run tests for task3\n", + "ctest --test-dir build -C Debug -R task3\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise1/modules b/exercise1/modules new file mode 160000 index 0000000..b8ce24c --- /dev/null +++ b/exercise1/modules @@ -0,0 +1 @@ +Subproject commit b8ce24c87fc76396de465b2573e19909fd10cf13 diff --git a/exercise1/task1.test.c b/exercise1/task1.test.c new file mode 100644 index 0000000..c4d2006 --- /dev/null +++ b/exercise1/task1.test.c @@ -0,0 +1,27 @@ +/// @file +/// @brief Task1: tests (support for C11 standard) + +#include // assert +#include // FILE +#include // EXIT_FAILURE|EXIT_SUCCESS + +#include "iue-io/ccsv.h" // https://sgit.iue.tuwien.ac.at/360050/modules + +int main(void) { + + FILE* stream = fopen("info_c.txt", "w"); + if (stream == NULL) + return EXIT_FAILURE; + + fprintf(stream, "This compilation unit was compiled\n"); + fprintf(stream, " - on %s at %s\n", __DATE__, __TIME__); + fprintf(stream, " - using the C language standard %li\n", __STDC_VERSION__); + fprintf(stream, " - the main-function was present in this file: %s\n", __FILE__); + fclose(stream); + + assert(__STDC_VERSION__ >= 201112L); + + printf("task1.test.c: all asserts passed\n"); + + return EXIT_SUCCESS; +} diff --git a/exercise1/task1.test.cpp b/exercise1/task1.test.cpp new file mode 100644 index 0000000..500f2e6 --- /dev/null +++ b/exercise1/task1.test.cpp @@ -0,0 +1,35 @@ +/// @file +/// @brief Task1: tests (support for C++20 standard) + +#include // assert +#include // std::filesystem::path +#include // std::ofstream +#include // std::cout|endl +#include // std::istringstream + +#include // https://en.cppreference.com/w/cpp/utility/feature_test + +#include "iue-io/csv.hpp" // https://sgit.iue.tuwien.ac.at/360050/modules + +int main() { + + std::ostringstream oss; + + oss << "This compilation unit was compiled" << std::endl; + oss << " - on " << __DATE__ << " at " << __TIME__ << std::endl; + oss << " - using the C++ language standard " << __cplusplus << std::endl; + oss << " - the main-function was present in this file: " << __FILE__ << std::endl; + + std::filesystem::path filename = "info_cpp.txt"; + std::ofstream ofs(filename); + ofs.exceptions(std::ios::failbit); + ofs << oss.str(); + + assert(__cplusplus >= 202002L); + assert(__cpp_lib_filesystem >= 201703L); + assert(__cpp_concepts >= 201907L); + + std::cout << "task1.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise1/task1.test.py b/exercise1/task1.test.py new file mode 100644 index 0000000..21dc786 --- /dev/null +++ b/exercise1/task1.test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +""" Task1: tests (support for python, numpy and matplotlib) """ + +import platform + +def os_info(): + """ Collects basic info on the operating system and logs it to a file 'os_info.txt'. """ + os_type = platform.system() + os_rel = platform.release() + os_ver = platform.version() + return f"os: {os_type} {os_rel} {os_ver}\n" + +def python_info(): + """ Obtains information on Python installation in use and logs it to a file 'python_info.txt'. """ + python_ver = platform.python_version() + python_impl = platform.python_implementation() + return f"py: {python_impl} {python_ver}\n" + +import numpy + +def numpy_info(): + """ Obtains information on the numpy package in use and logs it to a file 'numpy_info.txt'. """ + numpy_name = numpy.__name__ + numpy_ver = numpy.__version__ + return f"np: {numpy_name} {numpy_ver}\n" + +import matplotlib + +def matplotlib_info(): + """ Obtains information on the matplotlib package in use and logs it to a file 'matplotlib_info.txt'. """ + matplotlib_name = matplotlib.__name__ + matplotlib_ver = matplotlib.__version__ + return f"mpl: {matplotlib_name} {matplotlib_ver}\n" + +import unittest +import os + +class Test(unittest.TestCase): + + def test_python_env(self): + with open("info_python.txt", "w") as f: + f.write(os_info()) + f.write(python_info()) + f.write(numpy_info()) + f.write(matplotlib_info()) + +if __name__ == '__main__': + unittest.main() diff --git a/exercise1/task2.cpp b/exercise1/task2.cpp new file mode 100644 index 0000000..3b2395f --- /dev/null +++ b/exercise1/task2.cpp @@ -0,0 +1,25 @@ +/// @file +/// @brief Task2: "single-file" excutable C++ program + + +#include + +/// @brief Calculate the sum of two integer values +/// @param a first integer value +/// @param b second integer value +/// @return Sum of the two integer values +int sum(int a, int b) { + return a + b; +} + + +/// @brief main function (entry point) conducting the following tasks in this order +/// - create two local variables holding the integer values 33 and 6600 +/// - call your function 'sum' and provide the prepared variables as arguments to the call +/// - capture the result of your function call in a local variable and print it to the console +int main() { + int a = 33; + int b = 6600; + int result = sum(a, b); + std::cout << "The sum of " << a << " and " << b << " is " << result << std::endl; +} \ No newline at end of file diff --git a/exercise1/task3.cpp b/exercise1/task3.cpp new file mode 100644 index 0000000..8e478c4 --- /dev/null +++ b/exercise1/task3.cpp @@ -0,0 +1,65 @@ +/// @file +/// @brief Task3: implementation + +#include "task3.hpp" // add|mul|frac|mean|squared|cubed|eval + +/// @todo Include standard library headers as needed + +/// @brief Calculates the sum of three values +/// @param x 1st value +/// @param y 2nd value +/// @param z 3rd value +/// @return Sum of the three values +double add(double x, double y, double z) { + return x+y+z; +} + +/// @brief Calculates the product of three values +/// @param x 1st value +/// @param y 2nd value +/// @param z 3rd value +/// @return Product of the three values +double mul(double x, double y, double z) { + return x*y*z; +} + +/// @brief Calculates the fraction of two values +/// @param x 1st value +/// @param y 2nd value +/// @return Fraction of x divided by y +double frac(double x, double y) { + return x/y; +} + +/// @brief Calculates the average (arithmetic mean) of three values +/// @param x 1st value +/// @param y 2nd value +/// @param z 3rd value +/// @return Average of the three values +double mean(double x, double y, double z) { + return (x+y+z)/3; +} + +/// @brief Calculates the square of a value +/// @param x Value +/// @return Square of x +double squared(double x) { + return x*x; +} + +/// @brief Calculates the third power of a value +/// @param x Value +/// @return Cube of x +double cubed(double x) { + return x*x*x; +} + +/// @brief Evaluates a polynomial 'f(x) = a*x^2 + b*x + c' +/// @param x Variable +/// @param a Coefficient +/// @param b Coefficient +/// @param c Coefficient +/// @return Value of the polynomial at x +double eval(double x, double a, double b, double c) { + return a*x*x + b*x + c; +} diff --git a/exercise1/task3.hpp b/exercise1/task3.hpp new file mode 100644 index 0000000..42a57ee --- /dev/null +++ b/exercise1/task3.hpp @@ -0,0 +1,49 @@ +/// @file +/// @brief Task3: function declarations + +#pragma once + +/// @brief Calculates the sum of three values +/// @param x 1st value +/// @param y 2nd value +/// @param z 3rd value +/// @return Sum of the three values +double add(double x, double y, double z); + +/// @brief Calculates the product of three values +/// @param x 1st value +/// @param y 2nd value +/// @param z 3rd value +/// @return Product of the three values +double mul(double x, double y, double z); + +/// @brief Calculates the fraction of two values +/// @param x 1st value +/// @param y 2nd value +/// @return Fraction of x divided by y +double frac(double x, double y); + +/// @brief Calculates the average (arithmetic mean) of three values +/// @param x 1st value +/// @param y 2nd value +/// @param z 3rd value +/// @return Average of the three values +double mean(double x, double y, double z); + +/// @brief Calculates the square of a value +/// @param x Value +/// @return Square of x +double squared(double x); + +/// @brief Calculates the third power of a value +/// @param x Value +/// @return Cube of x +double cubed(double x); + +/// @brief Evaluates a polynomial 'f(x) = a*x^2 + b*x + c' +/// @param x Variable +/// @param a Coefficient +/// @param b Coefficient +/// @param c Coefficient +/// @return Value of the polynomial at x +double eval(double x, double a, double b, double c); diff --git a/exercise1/task3.test.cpp b/exercise1/task3.test.cpp new file mode 100644 index 0000000..c38fa39 --- /dev/null +++ b/exercise1/task3.test.cpp @@ -0,0 +1,44 @@ +/// @file +/// @brief Test for Task3 + +#include "task3.hpp" // add|mul|frac|mean|squared|cubed|eval + +#include // assert +#include // std::abs +#include // std::cout|endl + +int main() { + + { // testing function 'add' + double res = add(1, 2, -3); + assert(std::abs(res - 0.0) < 1e-7); + } + { // testing function 'mul' + double res = mul(2, 3, 4); + assert(std::abs(res - 24.0) < 1e-7); + } + { // testing function 'frac' + double res = frac(1, 3); + assert(std::abs(res - 1.0 / 3.0) < 1e-7); + } + { // testing function 'mean' + double res = mean(10, 90, -10); + assert(std::abs(res - 30.0) < 1e-7); + } + { // testing function 'squared' + double res = squared(3); + assert(std::abs(res - 9.0) < 1e-7); + } + { // testing function 'cubed' + double res = cubed(3); + assert(std::abs(res - 27.0) < 1e-7); + } + { // testing function 'cubed' + double res = eval(10.0, 3, -30, 77); + assert(std::abs(res - 77) < 1e-7); + } + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise10/.clang-format b/exercise10/.clang-format new file mode 100644 index 0000000..0c35b1b --- /dev/null +++ b/exercise10/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise10/.clangd b/exercise10/.clangd new file mode 100644 index 0000000..55e7d7b --- /dev/null +++ b/exercise10/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: + # - --target=x86_64-w64-windows-gnu + # - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] diff --git a/exercise10/.ctests b/exercise10/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise10/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise10/.expected-files b/exercise10/.expected-files new file mode 100644 index 0000000..fe16c65 --- /dev/null +++ b/exercise10/.expected-files @@ -0,0 +1,3 @@ +task1.main.c +task2.c +task3.main.c diff --git a/exercise10/.gitattributes b/exercise10/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise10/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise10/.gitignore b/exercise10/.gitignore new file mode 100644 index 0000000..63d24d2 --- /dev/null +++ b/exercise10/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix diff --git a/exercise10/.gitmodules b/exercise10/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/exercise10/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/exercise10/CMakeLists.txt b/exercise10/CMakeLists.txt new file mode 100644 index 0000000..cb26d2f --- /dev/null +++ b/exercise10/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise10 LANGUAGES C + DESCRIPTION "exercise10" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise10") + +# setting required language standards + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED True) + set(CMAKE_C_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# find math library and link to all targets + + find_library(MATH_LIBRARY m) + link_libraries(${MATH_LIBRARY}) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.c) + target_link_libraries(task1 PRIVATE ${MATH_LIBRARY}) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION ".*10.*") + + add_executable(task2 task2.c task2.test.c) + target_link_libraries(task2 PRIVATE ${MATH_LIBRARY}) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3_main task2.c task3.main.c) + + add_test(NAME task3_popt_fail COMMAND task3_main --left rrev4x4.csv --right matrix4x2.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_test(NAME task3_inpath_fail COMMAND task3_main --left hui.csv --right matrix4x2.csv --out result.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_test(NAME task3_dims_fail COMMAND task3_main --left matrix4x2.csv --right rrev4x4.csv --out result.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_test(NAME task3_outpath_fail COMMAND task3_main --left rrev4x4.csv --right matrix4x2.csv --out hui/result.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_test(NAME task3_csv_fail COMMAND task3_main --left rrev4x4.csv --right invalid.csv --out matrix4x2_rrow.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_test(NAME task3_rrow COMMAND task3_main --left rrev4x4.csv --right matrix4x2.csv --out matrix4x2_rrow.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_test(NAME task3_rcol COMMAND task3_main --left matrix4x2.csv --right rcol2x2.csv --out matrix4x2_rcols.csv WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3_test task3.test.c) + add_test(NAME task3_test COMMAND task3_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + set_tests_properties(task3_popt_fail task3_inpath_fail task3_dims_fail task3_outpath_fail task3_csv_fail PROPERTIES WILL_FAIL TRUE) + set_tests_properties(task3_test PROPERTIES DEPENDS task3_rrow) + set_tests_properties(task3_test PROPERTIES DEPENDS task3_rcol) + + add_custom_target(task3) + add_dependencies(task3 task3_test task3_main) + diff --git a/exercise10/README.md b/exercise10/README.md new file mode 100644 index 0000000..731e5f4 --- /dev/null +++ b/exercise10/README.md @@ -0,0 +1,19 @@ +# Hausübung 10 (3 Punkte) + +**Ausgabe**: Donnerstag 6. Juni 2024, vormittags. + +**Abgabe bis**: Montag 17. Juni 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise10`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- C: Kommandozeilen-Optionen +- C: Speicherung multidimensionaler (hier zweidimensional) Felder in einem kontinuierlichen Speicherbereich (hier *row-major*-Ordnung) +- C: Matrixoperationen basierend auf einer *row-major*-Ordnung + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise10/compile_flags.txt b/exercise10/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise10/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise10/invalid.csv b/exercise10/invalid.csv new file mode 100644 index 0000000..37fbe8c --- /dev/null +++ b/exercise10/invalid.csv @@ -0,0 +1,5 @@ +// matrix4x2.csv: a 4x2 matrix, not using '#' to indicate comments, non float data +not-a-number;11;12 +not-a-number;21;22 +not-a-number;31;32 +not-a-number;41;42 diff --git a/exercise10/main.ipynb b/exercise10/main.ipynb new file mode 100644 index 0000000..86b90ea --- /dev/null +++ b/exercise10/main.ipynb @@ -0,0 +1,203 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C-Programm (*row-major layout*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.c`](task1.main.c) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```c\n", + "\t#include // INT_MAX\n", + "\t#include // printf\n", + "\t...\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion, z.B.:\n", + "\t```cpp\n", + "\tsize_t func(...) {\n", + "\t ...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\t\n", + "\t int res = func(...);\n", + "\t ...\t\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.c`](task1.main.c)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.c`](task1.main.c)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Funktionalität für zweidimensionale Felder mit kontinuierlichem Speicherlayout (hier: *row-major order*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Gegeben ist eine Struktur `struct Matrix`, die eine *M x N*-Matrix mit kontinuierlichem Speicherlayout darstellt, ebenso gegeben sind drei yugehörige Funktionen:<>\n", + "```c\n", + "struct Matrix {\n", + " double* data; ///< pointer to a dynamically allocated contiguous memory block of size m*n\n", + " size_t m; ///< number of rows (first dimension)\n", + " size_t n; ///< number of colmns (second dimension)\n", + "};\n", + "\n", + "struct Matrix matrix_init(size_t m, size_t n, const double *data);\n", + "void matrix_print(const struct Matrix* mat);\n", + "void matrix_clear(struct Matrix* mat);\n", + "```\n", + "Sie implementieren weitere vier Funktionen, die die Funktionalität erweitern:\n", + "\n", + "```c\n", + "// todo: implement\n", + "struct Matrix matrix_zeros(size_t m, size_t n);\n", + "struct Matrix matrix_identity(size_t n);\n", + "void matrix_transpose(struct Matrix* mat);\n", + "void matrix_mult(const struct Matrix* a, const struct Matrix* b, struct Matrix* c);\n", + "```\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Strukturen/Funktionen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.h`](task2.h)\n", + "- Ihre Implementierung erfolgt in [`task2.c`](task2.c)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.c`](task2.test.c) " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Kommandozeilen-Programm Matrix/Matrix-Multiplikation (1 Punkt)\n", + "\n", + "Sie implementieren ein Programm, das \n", + "\n", + "- zwei Matrizen aus zwei `.csv`-Dateien einließt, \n", + "- das Produkt der Matrizen berechnet (Matrix/Matrix-Multiplikation), und\n", + "- das Ergebnis wiederum als `.csv`-Datei speichert.\n", + "\n", + "Die Dateinamen werden mittels der Kommandozeile übergeben.\n", + "\n", + "Das Programm bricht in folgenden Sitationen ab:\n", + "\n", + "- unzureichende Argumente\n", + "- invalide Dateipfade\n", + "- Fehler beim Einlesen der Dateien\n", + "- inkompatible Matrix-Dimensionen" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.main.c`](task3.main.c)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task3.main.c`](task3.main.c)\n", + "- Getestet wird Ihr Programm, indem es mit verschiedenen Parametern in der Kommandozeile aufgerufen wird (siehe auch [`CMakeLists.txt`](CMakeLists.txt) und im nächsten Abschnitt).\n", + "- Nachfolgend werden die Ausgabedateien der letzten beiden Aufrufe mit den Tests in [`task3.test.c`](task3.test.c) überprüft." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `gcc`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "gcc -g -std=c11 task1.main.c -o build/task1 -lm\n", + "gcc -g -Imodules -std=c11 task2.c task2.test.c -o build/task2 -lm\n", + "gcc -g -Imodules -std=c11 task2.c task3.main.c -o build/task3_main -lm\n", + "gcc -g -Imodules -std=c11 task3.test.c -o build/task3_test -lm\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\t\n", + "./build/task3_main --left rrev4x4.csv --right matrix4x2.csv # expect runtime fail: invalid arguments\n", + "./build/task3_main --left hui.csv --right matrix4x2.csv --out result.csv # expect runtime fail: invalid input filename\n", + "./build/task3_main --left matrix4x2.csv --right rrev4x4.csv --out result.csv # expect runtime fail: invalid matrix dimensions\n", + "./build/task3_main --left rrev4x4.csv --right matrix4x2.csv --out hui/result.csv # expect runtime fail: invalid output filename\n", + "./build/task3_main --left rrev4x4.csv --right invalid.csv --out matrix4x2_rrow.csv # expect runtime fail: invalid line in invalid.csv\n", + "./build/task3_main --left rrev4x4.csv --right matrix4x2.csv --out matrix4x2_rrow.csv # expected succeed and to generate matrix4x2_rrow.csv\n", + "./build/task3_main --left matrix4x2.csv --right rcol2x2.csv --out matrix4x2_rcols.csv # expected succeed and to generate matrix4x2_rcols.csv\n", + "./build/task3_test # tests the contents of the generated files matrix4x2_rrow.csv and matrix4x2_rcols.csv\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug # Windows\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -D CMAKE_C_FLAGS=\"-fsanitize=address\" # Linux\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 --verbose\n", + "ctest --test-dir build -C Debug -R task2 --verbose\n", + "ctest --test-dir build -C Debug -R task3 --verbose\n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise10/matrix4x2.csv b/exercise10/matrix4x2.csv new file mode 100644 index 0000000..aaf1285 --- /dev/null +++ b/exercise10/matrix4x2.csv @@ -0,0 +1,5 @@ +# matrix4x2.csv: a 4x2 matrix +11;12 +21;22 +31;32 +41;42 diff --git a/exercise10/matrix4x2_rcols.csv b/exercise10/matrix4x2_rcols.csv new file mode 100644 index 0000000..691bdcf --- /dev/null +++ b/exercise10/matrix4x2_rcols.csv @@ -0,0 +1,4 @@ +1.200000000000000000e+001;1.100000000000000000e+001 +2.200000000000000000e+001;2.100000000000000000e+001 +3.200000000000000000e+001;3.100000000000000000e+001 +4.200000000000000000e+001;4.100000000000000000e+001 diff --git a/exercise10/matrix4x2_rrow.csv b/exercise10/matrix4x2_rrow.csv new file mode 100644 index 0000000..bcd3914 --- /dev/null +++ b/exercise10/matrix4x2_rrow.csv @@ -0,0 +1,4 @@ +4.100000000000000000e+001;4.200000000000000000e+001 +3.100000000000000000e+001;3.200000000000000000e+001 +2.100000000000000000e+001;2.200000000000000000e+001 +1.100000000000000000e+001;1.200000000000000000e+001 diff --git a/exercise10/modules b/exercise10/modules new file mode 160000 index 0000000..33515ac --- /dev/null +++ b/exercise10/modules @@ -0,0 +1 @@ +Subproject commit 33515ac3ade8381f1336333051f85c7f63c8705c diff --git a/exercise10/rcol2x2.csv b/exercise10/rcol2x2.csv new file mode 100644 index 0000000..8802ba2 --- /dev/null +++ b/exercise10/rcol2x2.csv @@ -0,0 +1,3 @@ +# rcol2x2: reverses column order if right multiplied with any Mx2 matrix +0; 1 +1; 0 diff --git a/exercise10/rrev4x4.csv b/exercise10/rrev4x4.csv new file mode 100644 index 0000000..8010aa6 --- /dev/null +++ b/exercise10/rrev4x4.csv @@ -0,0 +1,5 @@ +# rrev4x4.csv: reverses row order if left multiplied to a 4xN matrix +0; 0; 0; 1 +0; 0; 1; 0 +0; 1; 0; 0 +1; 0; 0; 0 diff --git a/exercise10/task1.main.c b/exercise10/task1.main.c new file mode 100644 index 0000000..4576bdb --- /dev/null +++ b/exercise10/task1.main.c @@ -0,0 +1,67 @@ +/// @file +/// @brief Task1: "single-file" executable C program + +/// @todo Include C standard library headers, as needed + +/// @todo Implement a function 'max_column_sum' according to the description below: +/// The function receives a "m x n"-matrix holding signed integer values stored in a contiguous block of memory using +/// row-major layout. Specifically it receives these arguments +/// - number of rows m +/// - number of columns n +/// - pointer to a contiguous block of memory containing m*n signed integer values +/// - row-major storage order is used, access of element (i,j) -> data[j + n*i] +/// The function then calculates the maximum column sum, i.e. the maximum sum of values in one column of the matrix, and +/// returns this value. + +/// @todo Implement a 'main' function conducting the following tasks in this order: +/// - construct three local variables containing the following values: +/// - an integer value for the number of rows: 2 +/// - an integer value for the number of columns: 4 +/// - an array of integer values to be interpreted as a 2x4 matrix in row-major layout containing these values: +/// {5, -2, -12, 4, 1, 3, -5, 6} +/// - use your function to calculate the maximum column sum of the 2x4 matrix specified by your three local variables +/// - print the result to the console + + +/// @file +/// @brief Task1: "single-file" executable C program + +#include + +/// Function to calculate the maximum column sum of a matrix +int max_column_sum(int m, int n, int data[]) { + int max_sum = data[0]; // Initialize with first element + + // Loop through each column + for (int j = 1; j < n; j++) { + int current_sum = 0; + // Loop through each row in the current column + for (int i = 0; i < m; i++) { + // Calculate sum of elements in current column + current_sum += data[j + n * i]; + } + // Update max_sum if current sum is greater + if (current_sum > max_sum) { + max_sum = current_sum; + } + } + + return max_sum; +} + +int main() { + // Define matrix dimensions + int rows = 2; + int cols = 4; + + // Define matrix data in row-major order + int data[] = {5, -2, -12, 4, 1, 3, -5, 6}; + + // Calculate maximum column sum + int max_column_sum_value = max_column_sum(rows, cols, data); + + // Print the result + printf("Maximum column sum: %d\n", max_column_sum_value); + + return 0; +} diff --git a/exercise10/task2.c b/exercise10/task2.c new file mode 100644 index 0000000..7353104 --- /dev/null +++ b/exercise10/task2.c @@ -0,0 +1,102 @@ +/// @file +/// @brief Task2: function definitions + +#include "task2.h" // struct Matrix, matrix_mult + +#include // size_t +#include // printf +#include // malloc, free + +/// @todo Include C standard library headers as needed + +/// @note This implementation is provided as declared and specified in task2.h +struct Matrix matrix_init(size_t m, size_t n, const double* data) { + struct Matrix res = {.data = malloc(sizeof(double) * m * n), .m = m, .n = n}; + double* A = res.data; + for (size_t i = 0; i != m * n; ++i) + A[i] = data[i]; + return res; +} + +/// @note This implementation is provided as declared and specified in task2.h +void matrix_print(const struct Matrix* mat) { + size_t M = mat->m; + size_t N = mat->n; + const double* A = mat->data; + + for (size_t m = 0; m != M; ++m) { + for (size_t n = 0; n != N; ++n) + printf("%lf ", A[n + N * m]); + printf("\n"); + } + printf("\n"); +} + +/// @note This implementation is provided as declared and specified in task2.h +void matrix_clear(struct Matrix* mat) { + free(mat->data); + mat->m = 0; + mat->n = 0; +} + +/// @brief Initializes an matrix with zeros +/// @param m first dimension of the matrix +/// @param n first dimension of the matrix +struct Matrix matrix_zeros(size_t m, size_t n){ + double* data = malloc(sizeof(double) * m * n); + for (size_t i = 0; i != m * n; ++i) + data[i] = 0; + return matrix_init(m, n, data); +} + +/// @brief Initializes a square identity matrix +/// @param n dimension of the identity matrix +struct Matrix matrix_identity(size_t n){ + double* data = malloc(sizeof(double) * n * n); + for (size_t i = 0; i != n; ++i) + for (size_t j = 0; j != n; ++j) + data[j + n * i] = i == j ? 1 : 0; + return matrix_init(n, n, data); + +} + +/// @brief Transposes a matrix +/// @param mat Matrix to be transposed +/// @note this function might swap/replace the block of memory owned by the matrix +void matrix_transpose(struct Matrix* mat){ + size_t m = mat->m; + size_t n = mat->n; + double* data = malloc(sizeof(double) * m * n); + for (size_t i = 0; i != m; ++i) + for (size_t j = 0; j != n; ++j) + data[i + m * j] = mat->data[j + n * i]; + free(mat->data); + mat->data = data; + mat->m = n; + mat->n = m; + +} + +/// @brief Performs a matrix-matrix mutliplication: a*b = c +/// @a first matrix (left factor) +/// @b second matrix (right factor) +/// @c Result of the multiplication is stored in this Matrix: +/// - the dimensions of this matrix must be 'a.m x b.n' when calling this function +/// - the values will be overwritten with the result of the multiplication +void matrix_mult(const struct Matrix* a, const struct Matrix* b, struct Matrix* c){ + size_t m = a->m; + size_t n = a->n; + size_t p = b->n; + double* data = malloc(sizeof(double) * m * p); + for (size_t i = 0; i != m; ++i) + for (size_t j = 0; j != p; ++j){ + double sum = 0; + for (size_t k = 0; k != n; ++k) + sum += a->data[k + n * i] * b->data[j + p * k]; + data[j + p * i] = sum; + } + free(c->data); + c->data = data; + c->m = m; + c->n = p; +} \ No newline at end of file diff --git a/exercise10/task2.h b/exercise10/task2.h new file mode 100644 index 0000000..b5be830 --- /dev/null +++ b/exercise10/task2.h @@ -0,0 +1,51 @@ +/// @file +/// @brief Task2: Structure definitions and function declarations + +#pragma once + +#include // size_t + +/// @brief Two-dimensional matrix with 'm' rows and 'n' columns. +/// @note: the values ares stored in a contiguous block in memory in row-major layout +/// @note: row-major storage order is used, access of element (i,j) -> data[j + n*i] +struct Matrix { + double* data; ///< pointer to a dynamically allocated contiguous memory block fitting m*n values + size_t m; ///< number of rows (first dimension) + size_t n; ///< number of columns (second dimension) +}; + +/// @brief Initalize a matrix from the values in a buffer +/// @param m first dimension of the matrix +/// @param n second dimension of the matrix +/// @param data contiguous memory holding the values to copy (in row-major format) +struct Matrix matrix_init(size_t m, size_t n, const double* data); + +/// @brief Prints a Matrix to the console +/// @param mat Matrix to be printed +void matrix_print(const struct Matrix* mat); + +/// @brief Resets a matrix (deallocates memory and sets its size to 0 x 0) +/// @param mat Matrix to be reset +void matrix_clear(struct Matrix* mat); + +/// @brief Initializes an matrix with zeros +/// @param m first dimension of the matrix +/// @param n first dimension of the matrix +struct Matrix matrix_zeros(size_t m, size_t n); + +/// @brief Initializes a square identity matrix +/// @param n dimension of the identity matrix +struct Matrix matrix_identity(size_t n); + +/// @brief Transposes a matrix +/// @param mat Matrix to be transposed +/// @note this function might swap/replace the block of memory owned by the matrix +void matrix_transpose(struct Matrix* mat); + +/// @brief Performs a matrix-matrix mutliplication: a*b = c +/// @a first matrix (left factor) +/// @b second matrix (right factor) +/// @c Result of the multiplication is stored in this Matrix: +/// - the dimensions of this matrix must be 'a.m x b.n' when calling this function +/// - the values will be overwritten with the result of the multiplication +void matrix_mult(const struct Matrix* a, const struct Matrix* b, struct Matrix* c); diff --git a/exercise10/task2.test.c b/exercise10/task2.test.c new file mode 100644 index 0000000..c80bcd5 --- /dev/null +++ b/exercise10/task2.test.c @@ -0,0 +1,220 @@ +/// @file +/// @brief Task2: tests + +#include "task2.h" // struct Matrix, matrix_mult + +#include "modules/iue-num/numerics.h" // iuenum_isclose + +#include // assert +#include // bool, true, false +#include // printf + +// helper function +bool isclose(const struct Matrix* a, const struct Matrix* b) { + if (a->m != b->m) + return false; + if (a->n != b->n) + return false; + for (size_t i = 0; i != a->m; ++i) + for (size_t j = 0; j != a->n; ++j) + if (!iuenum_isclose(a->data[j + a->n * i], b->data[j + b->n * i])) + return false; + return true; +} + +int main() { + + { // testing 'matrix_zeros' for 3x3 + struct Matrix mat = matrix_zeros(3, 3); + double data[3][3] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; + struct Matrix expected = matrix_init(3, 3, &data[0][0]); + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_zeros' for 4x3 + struct Matrix mat = matrix_zeros(4, 3); + double data[4][3] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; + struct Matrix expected = matrix_init(4, 3, &data[0][0]); + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_identity' for 3x3 + + double data_expected[3][3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }; + struct Matrix expected = matrix_init(3, 3, &data_expected[0][0]); + struct Matrix mat = matrix_identity(3); + + // matrix_print(&mat); + assert(isclose(&mat, &expected)); + // matrix_print(&expected); + + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_identity' for 4x4 + + double data_expected[4][4] = { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1}, + }; + struct Matrix expected = matrix_init(4, 4, &data_expected[0][0]); + struct Matrix mat = matrix_identity(4); + + // matrix_print(&mat); + assert(isclose(&mat, &expected)); + // matrix_print(&expected); + + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_transpose' of a 3x3 identity + + struct Matrix mat = matrix_identity(3); + + double data_expected[3][3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }; + struct Matrix expected = matrix_init(3, 3, &data_expected[0][0]); + + // matrix_print(&mat); + matrix_transpose(&mat); + // matrix_print(&mat); + // matrix_print(&expected); + + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_transpose' for 4x2 + + double data[4][2] = { + {11, 12}, + {21, 22}, + {31, 32}, + {41, 42}, + }; + struct Matrix mat = matrix_init(4, 2, &data[0][0]); + + double data_expected[2][4] = { + {11, 21, 31, 41}, + {12, 22, 32, 42}, + }; + struct Matrix expected = matrix_init(2, 4, &data_expected[0][0]); + + // matrix_print(&mat); + matrix_transpose(&mat); + // matrix_print(&mat); + // matrix_print(&expected); + + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_mult' using a left multiplty with a row permuting matrix + + double data[4][2] = { + {11, 12}, + {21, 22}, + {31, 32}, + {41, 42}, + }; + struct Matrix mat = matrix_init(4, 2, &data[0][0]); + + double data_permute[4][4] = { + {0, 0, 0, 1}, + {0, 0, 1, 0}, + {0, 1, 0, 0}, + {1, 0, 0, 0}, + }; + struct Matrix permute = matrix_init(4, 4, &data_permute[0][0]); + + double data_expected[4][2] = { + {41, 42}, + {31, 32}, + {21, 22}, + {11, 12}, + }; + struct Matrix expected = matrix_init(4, 2, &data_expected[0][0]); + + struct Matrix product = matrix_zeros(permute.m, mat.n); + + // matrix_print(&permute); + // matrix_print(&mat); + matrix_mult(&permute, &mat, &product); // left multiply with permuation matrix + // matrix_print(&product); + + assert(isclose(&product, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + matrix_clear(&permute); + matrix_clear(&product); + } + + + { // testing 'matrix_mult' using a right multiplty with a column permuting matrix + + double data[4][2] = { + {11, 12}, + {21, 22}, + {31, 32}, + {41, 42}, + }; + struct Matrix mat = matrix_init(4, 2, &data[0][0]); + + double data_permute[2][2] = { + {0, 1}, + {1, 0}, + }; + struct Matrix permute = matrix_init(2, 2, &data_permute[0][0]); + + double data_expected[4][2] = { + {12, 11}, + {22, 21}, + {32, 31}, + {42, 41}, + }; + struct Matrix expected = matrix_init(4, 2, &data_expected[0][0]); + + struct Matrix product = matrix_zeros(mat.m, permute.n); + + // matrix_print(&permute); + // matrix_print(&mat); + matrix_mult(&mat, &permute, &product); // right multiply with permuation matrix + // matrix_print(&product); + + assert(isclose(&product, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + matrix_clear(&permute); + matrix_clear(&product); + } + + printf("task2.test.c: all asserts passed\n"); + + return 0; +} diff --git a/exercise10/task3.main.c b/exercise10/task3.main.c new file mode 100644 index 0000000..30ec2c7 --- /dev/null +++ b/exercise10/task3.main.c @@ -0,0 +1,196 @@ +/// @file +/// @brief Task3: program with command line options + +/// @todo Include header from modules/iue-*, as needed +/// e.g. #include "iue-po/cpo.h" +/// e.g. #include "iue-io/ccsv.h" + +/// @todo Include header of task2, if needed +// #include "task2.h" + +/// @todo Include C standard library headers, as needed +/// e.g. #include // EXIT_FAILURE, EXIT_SUCCESS + +/// @todo Implement an executable program which the following top-level description: +/// 1. reads two matrices 'L', and 'R' from two separate .csv-files +/// 2. calculates the matrix product LR (i.e. a matrix-matrix multiplication) +/// 3. stores the resulting matrix in a third .csv-file +/// Detailed Requirements: +/// 1. All .csv-files which are involved need to be compatible with +/// the format supported by 'iueio_savetxt' and 'iueio_loadtxt' from the header 'iue-io/ccsv.h' +/// using ';' as delimiter and '#' as comment +/// 2. The program needs to support the following three mandatory command line arguments in arbitrary order: +/// --left relative filepath to the 'L' matrix +/// --right relative filepath to the 'R' matrix +/// --out relative filepath for the produced result +/// 3. The program prints an error message to stderr, terminates, and returns EXIT_FAILURE if +/// a) If any of the mandatory arguments is missing +/// b) If any of the provided filepaths is not valid +/// c) If any of the provided files cannot be parsed successfully +/// d) If the dimension of the provided matrices are not compatible +/// 4. If the program finishes without any issue, it returns EXIT_SUCCESS +/// +/// Implementation hints/valid assumtions for this exercise: +/// - you do not need to support empty .csv-files +/// - you can always assume that all rows in a .csv-file have the same length + +/// @file +/// @brief Task3: program with command line options + +#include "modules/iue-io/ccsv.h" +#include +#include +#include +#include +#define DELIM ';' +#define COMMENT '#' + +// Function to multiply two matrices +double** multiply_matrices(double** L, size_t L_rows, size_t L_cols, double** R, size_t R_rows, size_t R_cols, size_t* out_rows, size_t* out_cols) { + if (L_cols != R_rows) { + return NULL; // Incompatible dimensions + } + + *out_rows = L_rows; + *out_cols = R_cols; + + double** result = malloc(L_rows * sizeof(double*)); + for (size_t i = 0; i < L_rows; ++i) { + result[i] = malloc(R_cols * sizeof(double)); + for (size_t j = 0; j < R_cols; ++j) { + result[i][j] = 0.0; + for (size_t k = 0; k < L_cols; ++k) { + result[i][j] += L[i][k] * R[k][j]; + } + } + } + + return result; +} + +int main(int argc, char* argv[]) { + char* left_filepath = NULL; + char* right_filepath = NULL; + char* out_filepath = NULL; + bool left_set = false; + bool right_set = false; + bool out_set = false; + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--left") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Missing argument for --left\n"); + return EXIT_FAILURE; + } + left_filepath = argv[i + 1]; + left_set = true; + i++; + } else if (strcmp(argv[i], "--right") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Missing argument for --right\n"); + return EXIT_FAILURE; + } + right_filepath = argv[i + 1]; + right_set = true; + i++; + } else if (strcmp(argv[i], "--out") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Missing argument for --out\n"); + return EXIT_FAILURE; + } + out_filepath = argv[i + 1]; + out_set = true; + i++; + } + } + + // Check for missing mandatory arguments + if (!left_set || !right_set || !out_set) { + fprintf(stderr, + "Missing mandatory arguments. Usage: %s --left --right --out " + "\n", + argv[0]); + return EXIT_FAILURE; + } + + // Load matrices from files + struct Table left_table = {NULL, 0}; + struct Table right_table = {NULL, 0}; + if (iueio_loadtxt(left_filepath, &left_table, DELIM, COMMENT) != 0) { + fprintf(stderr, "Error loading file %s\n", left_filepath); + return EXIT_FAILURE; + } + if (iueio_loadtxt(right_filepath, &right_table, DELIM, COMMENT) != 0) { + fprintf(stderr, "Error loading file %s\n", right_filepath); + return EXIT_FAILURE; + } + + // Check if the matrices are compatible + //if (left_table.n == 0 || right_table.n == 0 || left_table.rows[0].n != right_table.rows[0].n) { + // fprintf(stderr, "Incompatible dimensions\n"); + // return EXIT_FAILURE; + //} + + // Convert tables to matrices + size_t L_rows = left_table.n; + size_t L_cols = left_table.rows[0].n; + double** L = malloc(L_rows * sizeof(double*)); + for (size_t i = 0; i < L_rows; ++i) { + L[i] = malloc(L_cols * sizeof(double)); + for (size_t j = 0; j < L_cols; ++j) { + L[i][j] = left_table.rows[i].values[j]; + } + } + + size_t R_rows = right_table.n; + size_t R_cols = right_table.rows[0].n; + double** R = malloc(R_rows * sizeof(double*)); + for (size_t i = 0; i < R_rows; ++i) { + R[i] = malloc(R_cols * sizeof(double)); + for (size_t j = 0; j < R_cols; ++j) { + R[i][j] = right_table.rows[i].values[j]; + } + } + + // Multiply matrices + size_t out_rows, out_cols; + double** result = multiply_matrices(L, L_rows, L_cols, R, R_rows, R_cols, &out_rows, &out_cols); + if (result == NULL) { + fprintf(stderr, "Incompatible dimensions\n"); + return EXIT_FAILURE; + } + + // Convert result to table + struct Table out_table = {NULL, 0}; + for (size_t i = 0; i < out_rows; ++i) { + table_append_copy(&out_table, result[i], out_cols); + } + + // Save result to file + if (iueio_savetxt(out_filepath, &out_table, DELIM, "", COMMENT) != 0) { + fprintf(stderr, "Error saving file %s\n", out_filepath); + return EXIT_FAILURE; + } + + // Free memory + for (size_t i = 0; i < L_rows; ++i) { + free(L[i]); + } + free(L); + for (size_t i = 0; i < R_rows; ++i) { + free(R[i]); + } + free(R); + for (size_t i = 0; i < out_rows; ++i) { + free(result[i]); + } + free(result); + table_clear(&left_table); + table_clear(&right_table); + table_clear(&out_table); + + return EXIT_SUCCESS; +} + + diff --git a/exercise10/task3.test.c b/exercise10/task3.test.c new file mode 100644 index 0000000..bcf619e --- /dev/null +++ b/exercise10/task3.test.c @@ -0,0 +1,88 @@ +/// @file +/// @brief Task3: tests of output files generated by other tests/commands using command line arguments for task3_main + +#include "modules/iue-io/ccsv.h" // iueio_loadtxt +#include "modules/iue-num/numerics.h" // iuenum_isclose + +#include // assert +#include // bool, true, false +#include // printf + +bool isclose(const struct Table* a, const struct Table* b) { + if (a->n != b->n) + return false; + + for (size_t r = 0; r != a->n; ++r) { + if (a->rows[r].n != a->rows[r].n) + return false; + for (size_t c = 0; c != a->rows[r].n; ++c) + if (!iuenum_isclose(a->rows[r].values[c], b->rows[r].values[c])) + return false; + } + return true; +} + +int main() { + + { // testing result of this command: + // "./build/task3_main --left rrev4x4.csv --right matrix4x2.csv --out matrix4x2_rrow.csv" + + double data_expected[4][2] = { + {41, 42}, + {31, 32}, + {21, 22}, + {11, 12}, + }; + + struct Table expected = {NULL, 0}; + table_append_copy(&expected, data_expected[0], 2); + table_append_copy(&expected, data_expected[1], 2); + table_append_copy(&expected, data_expected[2], 2); + table_append_copy(&expected, data_expected[3], 2); + + const char filepath[] = "matrix4x2_rrow.csv"; + struct Table table = {NULL, 0}; + + if (iueio_loadtxt(filepath, &table, ';', '#') != 0) { + fprintf(stderr, "error loading file %s\n", filepath); + exit(EXIT_FAILURE); + } + + assert(isclose(&table, &expected)); + table_clear(&table); + table_clear(&expected); + } + + { // testing result of this command: + // "./build/task3_main --left matrix4x2.csv --right rcol2x2.csv --out matrix4x2_rcols.csv" + + double data_expected[4][2] = { + {12, 11}, + {22, 21}, + {32, 31}, + {42, 41}, + }; + + struct Table expected = {NULL, 0}; + table_append_copy(&expected, data_expected[0], 2); + table_append_copy(&expected, data_expected[1], 2); + table_append_copy(&expected, data_expected[2], 2); + table_append_copy(&expected, data_expected[3], 2); + + const char filepath[] = "matrix4x2_rcols.csv"; + struct Table table = {NULL, 0}; + + if (iueio_loadtxt(filepath, &table, ';', '#') != 0) { + fprintf(stderr, "error loading file %s\n", filepath); + exit(EXIT_FAILURE); + } + + assert(isclose(&table, &expected)); + table_clear(&table); + table_clear(&expected); + } + + printf("task3.test.c: all asserts passed\n"); + + return 0; +} diff --git a/exercise2/.clang-format b/exercise2/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/exercise2/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise2/.clangd b/exercise2/.clangd new file mode 100644 index 0000000..68a6e4c --- /dev/null +++ b/exercise2/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: +# - --target=x86_64-w64-windows-gnu +# - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/exercise2/.ctests b/exercise2/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise2/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise2/.expected-files b/exercise2/.expected-files new file mode 100644 index 0000000..7ec8475 --- /dev/null +++ b/exercise2/.expected-files @@ -0,0 +1,3 @@ +task1.main.cpp +task2.cpp +task3.cpp diff --git a/exercise2/.gitattributes b/exercise2/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise2/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise2/.gitignore b/exercise2/.gitignore new file mode 100644 index 0000000..a01b1e5 --- /dev/null +++ b/exercise2/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/exercise2/CMakeLists.txt b/exercise2/CMakeLists.txt new file mode 100644 index 0000000..e86ae48 --- /dev/null +++ b/exercise2/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise2 LANGUAGES CXX + DESCRIPTION "exercise2" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise2") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# include own targets + + add_executable(task1 task1.main.cpp) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PROPERTY_REGULAR_EXPRESSION "-1024") + + add_executable(task2 task2.cpp task2.test.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3 task3.cpp task3.test.cpp) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) diff --git a/exercise2/README.md b/exercise2/README.md new file mode 100644 index 0000000..5878c83 --- /dev/null +++ b/exercise2/README.md @@ -0,0 +1,20 @@ +# Hausübung 2 (3 Punkte) + +**Ausgabe**: Donnerstag 14. März 2024, vormittags. + +**Abgabe bis**: Montag 08. April 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise2`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfließen: + +- Bedingungen/Verzweigungen +- Schleifen +- Übergabe/Rückgabe/Manipulation von Sequenzen in Form eines `std::vector` und `std::vector` +- Übergabe und Rückgabewerte mehrerer Werte mittels `std::tuple` und `std::tuple` + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise2/compile_flags.txt b/exercise2/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise2/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise2/gitblah-sHB2 b/exercise2/gitblah-sHB2 new file mode 100644 index 0000000..efbbbe8 --- /dev/null +++ b/exercise2/gitblah-sHB2 @@ -0,0 +1,2 @@ +2024-04-08T09:19:08+02:00 | b8f93888363b8ac94476d28a5f7e0b85485a23ec | who has two thumbs and is a genius? not this guy! +2024-04-08T09:17:30+02:00 | 0d8d68a4679f8e4b48027a25da4280ec914cb6ab | Fingers crossed! \ No newline at end of file diff --git a/exercise2/main.ipynb b/exercise2/main.ipynb new file mode 100644 index 0000000..6f93df4 --- /dev/null +++ b/exercise2/main.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cheatsheets\n", + "\n", + "- [exercise1](https://sgit.iue.tuwien.ac.at/360050/cheatsheet/raw/branch/master/exercise1.pdf)\n", + "- [exercise2](https://sgit.iue.tuwien.ac.at/360050/cheatsheet/raw/branch/master/exercise2.pdf)\n", + "\n", + "## Aufgabe 1: Ein eigenes kleines C++-Programm (*vector in/ scalar out*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::cout, std::endl\n", + "\t#include <...>\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion, z.B.:\n", + "\t```cpp\n", + "\tint func(...){\n", + "\t ...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion (Einstiegspunkt für jedes lauffähige Programm), die Ihre selbst geschriebene Funktion verwendet und die berechneten Ergebnisse in der Konsole ausgibt, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\n", + "\t auto res = func(...)\t\n", + "\t std::cout << res << std::endl;\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Funktion mit Sequenzen von Werten als Parameter (`std::vector`), internen Verzweigungen (`if`/`else`), und mehreren Rückgabewerten (`std::tuple`) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie die folgenden Funktionen:\n", + "\n", + "```cpp\n", + "// type aliases\n", + "using Vector = std::vector;\n", + "using Tuple2 = std::tuple;\n", + "\n", + "int count_gt(Vector data, double ref);\n", + "int count_lt(Vector data, double ref);\n", + "\n", + "Vector select_gt(Vector data, double ref);\n", + "Vector select_lt(Vector data, double ref);\n", + "Vector select_gt_and_lt(Vector data, double lower, double upper);\n", + "\n", + "double mean(Vector data);\n", + "double median(Vector data);\n", + "\n", + "Tuple2 minmax(Vector data);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklarationen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.hpp`](task2.hpp)\n", + "- Ihre Implementierung erfolgt in [`task2.cpp`](task2.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Kapselung einer Berechnung mittels einer Funktion mit mehreren Rückgabewerten (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sie kapseln die Berechnung der Lösungen zu einer quadratischen Gleichung $ax^2 + bx + c = 0$ in einer Funktion.\n", + "\n", + "\n", + "Implementieren Sie folgenden beiden Funktionen (überladener Funktionsname):\n", + "\n", + "```cpp\n", + "std::tuple solve_quadratic_equation(double a, double b, double c);\n", + "std::tuple solve_quadratic_equation(std::tuple abc);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `g++`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "g++ -g -std=c++20 task1.main.cpp -o build/task1.exe\n", + "g++ -g -std=c++20 task2.cpp task2.test.cpp -o build/task2.exe\n", + "g++ -g -std=c++20 task3.cpp task3.test.cpp -o build/task3.exe\n", + "# run tests \n", + "./build/task1.exe\n", + "./build/task2.exe\n", + "./build/task3.exe\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile \n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "# run tests \n", + "ctest --test-dir build -C Debug -R task1\n", + "ctest --test-dir build -C Debug -R task2\n", + "ctest --test-dir build -C Debug -R task3\n", + "```\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise2/task1.main.cpp b/exercise2/task1.main.cpp new file mode 100644 index 0000000..8a32940 --- /dev/null +++ b/exercise2/task1.main.cpp @@ -0,0 +1,33 @@ +/// @file +/// @brief Task1: "single-file" excutable C++ program + +/// @todo Include standard library headers as needed + +#include +#include + +/// @brief Find the minimum value in a sequence of integer values +/// @param data Sequence of values stored in a std::vector (assertion: sequence is not empty ) +/// @return Minimum value in the sequence +int min(const std::vector& data) { + int min = data[0]; + for (int i = 1; i < data.size(); i++) { + if (data[i] < min) { + min = data[i]; + } + } + return min; +} + +/// @brief main function (entry point) conducting the following tasks in this order: +/// - create and prepare a local variable of type std::vector +/// holding a sequence of 5 values in this order: -10, 20, 100, -1024, 2048 +/// - call your function 'min' and provide the prepared variable as argument to the call +/// - capture the result of your function call in a local variable and print it to the console + +int main() { + std::vector data = {-10, 20, 100, -1024, 2048}; + int min_value = min(data); + std::cout << "The minimum value is: " << min_value << std::endl; + return 0; +} \ No newline at end of file diff --git a/exercise2/task2.cpp b/exercise2/task2.cpp new file mode 100644 index 0000000..78d3ddc --- /dev/null +++ b/exercise2/task2.cpp @@ -0,0 +1,117 @@ +#include "task2.hpp" // count_gt|count_lt|select_gt|select_lt|select_gt_and_lt|mean|median|minmax +#include + +/// @todo Include standard library headers as needed + +/// @brief Counts how many values in a sequence are greater than (gt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Number of values in the sequence, which are greater than than ref +int count_gt(std::vector data, double ref) { + int count = 0; + for (int i = 0; i < data.size(); i++) { + if (data[i] > ref) { + count++; + } + } + return count; +} + +/// @brief Counts how many values in a sequence are less than (lt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Number of values in the sequence, which are less than than ref +int count_lt(std::vector data, double ref) { + int count = 0; + for (int i = 0; i < data.size(); i++) { + if (data[i] < ref) { + count++; + } + } + return count; +} + +/// @brief Selects values from a sequence which are greater than (gt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Sequence of selected (copied) values in the order of occurence in the original sequence +std::vector select_gt(std::vector data, double ref) { + std::vector selected; + for (int i = 0; i < data.size(); i++) { + if (data[i] > ref) { + selected.push_back(data[i]); + } + } + return selected; +} + +/// @brief Selects values from a sequence which are less than (lt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Sequence of selected (copied) values in the order of occurence in the original sequence +std::vector select_lt(std::vector data, double ref) { + std::vector selected; + for (int i = 0; i < data.size(); i++) { + if (data[i] < ref) { + selected.push_back(data[i]); + } + } + return selected; +} + +/// @brief Selects values from a sequence which are bounded by two reference values +/// @param data Sequence of values +/// @param lower Lower bound +/// @param upper Upper bound +/// @return Sequence of selected (copied) values in the order of occurence in the original sequence +std::vector select_gt_and_lt(std::vector data, double lower, double upper) { + std::vector selected; + for (int i = 0; i < data.size(); i++) { + if (data[i] > lower && data[i] < upper) { + selected.push_back(data[i]); + } + } + return selected; +} + +/// @brief Calculates the mean for a sequence of values +/// @param data Sequence of values; assertion: data.size() >= 1 +/// @return Mean value (arithmetic mean) +double mean(std::vector data) { + double sum = 0; + for (int i = 0; i < data.size(); i++) { + sum += data[i]; + } + return sum / data.size(); +} + +/// @brief Calculate the median for a sequence of numbers +/// @param data Sequence of values; assertion: data.size() >= 1 +/// @return Median value: +/// - if the length of the sequence is odd, the median value is "the middle value", else +/// - if the length of the sequence is even, the median value is the avarage of the "two middle values" +double median(std::vector data) { + std::sort(data.begin(), data.end()); + if (data.size() % 2 == 0) { + return (data[data.size() / 2 - 1] + data[data.size() / 2]) / 2; + } else { + return data[data.size() / 2]; + } +} + +/// @brief Finds the minimum and maximum value +/// @param data Sequence of values; assertion: data.size() >= 1 +/// @return Tuple with the minimum and maximum value (in this order) +std::tuple minmax(std::vector data) { + double min = data[0]; + double max = data[0]; + for (int i = 1; i < data.size(); i++) { + if (data[i] < min) { + min = data[i]; + } + if (data[i] > max) { + max = data[i]; + } + } + return std::make_tuple(min, max); +} \ No newline at end of file diff --git a/exercise2/task2.hpp b/exercise2/task2.hpp new file mode 100644 index 0000000..9d00e5c --- /dev/null +++ b/exercise2/task2.hpp @@ -0,0 +1,55 @@ +/// @file +/// @brief Task2: function declarations + +#pragma once + +#include // std::tuple +#include // std::vector + +/// @brief Counts how many values in a sequence are greater than (gt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Number of values in the sequence, which are greater than than ref +int count_gt(std::vector data, double ref); + +/// @brief Counts how many values in a sequence are less than (lt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Number of values in the sequence, which are less than than ref +int count_lt(std::vector data, double ref); + +/// @brief Selects values from a sequence which are greater than (gt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Sequence of selected (copied) values in the order of occurence in the original sequence +std::vector select_gt(std::vector data, double ref); + +/// @brief Selects values from a sequence which are less than (lt) a reference value +/// @param data Sequence of values +/// @param ref Reference value +/// @return Sequence of selected (copied) values in the order of occurence in the original sequence +std::vector select_lt(std::vector data, double ref); + +/// @brief Selects values from a sequence which are bounded by two reference values +/// @param data Sequence of values +/// @param lower Lower bound +/// @param upper Upper bound +/// @return Sequence of selected (copied) values in the order of occurence in the original sequence +std::vector select_gt_and_lt(std::vector data, double lower, double upper); + +/// @brief Calculates the mean for a sequence of values +/// @param data Sequence of values; assertion: data.size() >= 1 +/// @return Mean value (arithmetic mean) +double mean(std::vector data); + +/// @brief Calculate the median for a sequence of numbers +/// @param data Sequence of values; assertion: data.size() >= 1 +/// @return Median value: +/// - if the length of the sequence is odd, the median value is "the middle value", else +/// - if the length of the sequence is even, the median value is the avarage of the "two middle values" +double median(std::vector data); + +/// @brief Finds the minimum and maximum value +/// @param data Sequence of values; assertion: data.size() >= 1 +/// @return Tuple with the minimum and maximum value (in this order) +std::tuple minmax(std::vector data); diff --git a/exercise2/task2.test.cpp b/exercise2/task2.test.cpp new file mode 100644 index 0000000..f912663 --- /dev/null +++ b/exercise2/task2.test.cpp @@ -0,0 +1,112 @@ +/// @file +/// @brief Test for Task2 + +#include "task2.hpp" // count_gt|count_lt|select_gt|select_lt|select_gt_and_lt|mean|median|minmax + +#include // assert +#include // std::cout|endl + +int main() { + + { // testing function 'count_gt' + int res = count_gt({1.0, 2.0, 3.0, 4.0}, 2.0); + assert(res == 2); + } + + { // testing function 'count_gt' + int res = count_gt({-4.0, -2.0, -2.0, -1.0}, -2.0); + assert(res == 1); + } + + { // testing function 'count_lt' + int res = count_lt({-4.0, -2.0, -2.0, -1.0}, -1.0); + assert(res == 3); + } + + { // testing function 'count_lt' + int res = count_lt({2.0, 2.0, 2.0, 2.0}, 3.0); + assert(res == 4); + } + + { // testing function 'select_lt' + std::vector res = select_lt({2.0, 2.0, 2.0, 2.0}, 3.0); + std::vector expected = {2.0, 2.0, 2.0, 2.0}; + assert(res == expected); + } + + { // testing function 'select_lt' + std::vector res = select_lt({1.0, 2.0, 3.0, 4.0}, 4.0); + std::vector expected = {1.0, 2.0, 3.0}; + assert(res == expected); + } + + { // testing function 'select_gt' + std::vector res = select_gt({2.0, 2.0, 2.0, 2.0}, 2.0); + std::vector expected = {}; + assert(res == expected); + } + + { // testing function 'select_gt' + std::vector res = select_gt({1.0, 2.0, 3.0, 4.0}, 2.0); + std::vector expected = {3.0, 4.0}; + assert(res == expected); + } + + { // testing function 'select_gt_and_lt' + std::vector res = select_gt_and_lt({-3.0, 2.0, -3.0, -2.0, 4.0, 1.0, -1.0, -4.0}, -3.0, 3.0); + std::vector expected = {2.0, -2.0, 1.0, -1.0}; + assert(res == expected); + } + + { // testing function 'mean' + double res = mean({1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 0.0}); + double expected = 3.0; + assert(std::abs(res - expected) < 1e-7); + } + + { // testing function 'mean' + double res = mean({-3.0, -2.0, -1.0, 1.0, 2.0, 3.0}); + double expected = 0.0; + assert(std::abs(res - expected) < 1e-7); + } + + { // testing function 'median' + double res = median({3.0}); + double expected = 3.0; + assert(std::abs(res - expected) < 1e-7); + } + + { // testing function 'median' + double res = median({2.0, 3.0}); + double expected = 2.5; + assert(std::abs(res - expected) < 1e-7); + } + + { // testing function 'median' + double res = median({3.0, 2.0, 3.0, 2.0}); + double expected = 2.5; + assert(std::abs(res - expected) < 1e-7); + } + + { // testing function 'median' + double res = median({3.0, 3.0, 3.0, -1.0, 4.0}); + double expected = 3.0; + assert(std::abs(res - expected) < 1e-7); + } + + { // testing function 'minmax' + std::tuple res = minmax({300.0, 100.0}); + std::tuple expected = {100.0, 300.0}; + assert(res == expected); + } + + { // testing function 'minmax' + std::tuple res = minmax({-1.0, -2.0, -3.0, 4.0, 20.0, 12.0}); + std::tuple expected = {-3.0, 20.0}; + assert(res == expected); + } + + std::cout << "task2.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise2/task3.cpp b/exercise2/task3.cpp new file mode 100644 index 0000000..c69e2ec --- /dev/null +++ b/exercise2/task3.cpp @@ -0,0 +1,33 @@ +#include "task3.hpp" // solve_quadratic_equation +#include + +/// @brief Calculates the real solutions of the quadratic equation a*x^2 + b*x + c = 0. +/// @param a coefficient; assertion: 'a' is a non-zero value +/// @param b coefficient +/// @param c coefficient +/// @return The two real solutions (order: ascending). +/// If no real solutions exists, the tuple contains two quiet NaNs. +std::tuple solve_quadratic_equation(double a, double b, double c){ + double x1, x2; + double d = b*b - 4*a*c; + if(d < 0){ + x1 = NAN; + x2 = NAN; + }else{ + x1 = (-b - sqrt(d))/(2*a); + x2 = (-b + sqrt(d))/(2*a); + } + return std::make_tuple(x1, x2); + +} + +/// @brief Calculates the real solutions of the quadratic equation a*x^2 + b*x + c = 0. +/// @param abc coefficients; assertion: first coefficient 'a' is a non-zero value +/// @return The two real solutions (order: ascending). +/// If no real solutions exists, the tuple contains two quiet NaNs. +std::tuple solve_quadratic_equation(std::tuple abc){ + double a = std::get<0>(abc); + double b = std::get<1>(abc); + double c = std::get<2>(abc); + return solve_quadratic_equation(a, b, c); +} \ No newline at end of file diff --git a/exercise2/task3.hpp b/exercise2/task3.hpp new file mode 100644 index 0000000..d6a6e59 --- /dev/null +++ b/exercise2/task3.hpp @@ -0,0 +1,20 @@ +/// @file +/// @brief Task3: function declarations + +#pragma once + +#include // std::tuple + +/// @brief Calculates the real solutions of the quadratic equation a*x^2 + b*x + c = 0. +/// @param a coefficient; assertion: 'a' is a non-zero value +/// @param b coefficient +/// @param c coefficient +/// @return The two real solutions (order: ascending). +/// If no real solutions exists, the tuple contains two quiet NaNs. +std::tuple solve_quadratic_equation(double a, double b, double c); + +/// @brief Calculates the real solutions of the quadratic equation a*x^2 + b*x + c = 0. +/// @param abc coefficients; assertion: first coefficient 'a' is a non-zero value +/// @return The two real solutions (order: ascending). +/// If no real solutions exists, the tuple contains two quiet NaNs. +std::tuple solve_quadratic_equation(std::tuple abc); diff --git a/exercise2/task3.test.cpp b/exercise2/task3.test.cpp new file mode 100644 index 0000000..08d51b2 --- /dev/null +++ b/exercise2/task3.test.cpp @@ -0,0 +1,54 @@ +/// @file +/// @brief Test for Task3 + +#include "task3.hpp" // solve_quadratic_equation + +#include // assert +#include // NAN +#include // std::cout|endl + +int main() { + + { // testing function overloads 'solve_quadratic_equation' + auto [s1, s2] = solve_quadratic_equation(1.0, 3.0, 2.0); + assert(std::abs(s1 - (-2.0)) < 1e-7); + assert(std::abs(s2 - (-1.0)) < 1e-7); + } + + { // testing function overloads 'solve_quadratic_equation' + std::tuple coefficients = {1.0, 3.0, 2.0}; + auto [s1, s2] = solve_quadratic_equation(coefficients); + assert(std::abs(s1 - (-2.0)) < 1e-7); + assert(std::abs(s2 - (-1.0)) < 1e-7); + } + + { // testing function overloads 'solve_quadratic_equation' + auto [s1, s2] = solve_quadratic_equation(1.0, 2.0, 5.0); + assert(std::isnan(s1)); + assert(std::isnan(s2)); + } + + { // testing function overloads 'solve_quadratic_equation' + std::tuple coefficients = {1.0, 2.0, 5.0}; + auto [s1, s2] = solve_quadratic_equation(coefficients); + assert(std::isnan(s1)); + assert(std::isnan(s2)); + } + + { // testing function overloads 'solve_quadratic_equation' + auto [s1, s2] = solve_quadratic_equation(2.0, 1.0, -3.0); + assert(std::abs(s1 - (-1.5)) < 1e-7); + assert(std::abs(s2 - (1.0)) < 1e-7); + } + + { // testing function overloads 'solve_quadratic_equation' + std::tuple coefficients = {2.0, 1.0, -3.0}; + auto [s1, s2] = solve_quadratic_equation(coefficients); + assert(std::abs(s1 - (-1.5)) < 1e-7); + assert(std::abs(s2 - (1.0)) < 1e-7); + } + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise3/.clang-format b/exercise3/.clang-format new file mode 100644 index 0000000..0c35b1b --- /dev/null +++ b/exercise3/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise3/.clangd b/exercise3/.clangd new file mode 100644 index 0000000..68a6e4c --- /dev/null +++ b/exercise3/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: +# - --target=x86_64-w64-windows-gnu +# - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/exercise3/.ctests b/exercise3/.ctests new file mode 100644 index 0000000..4dbb5d5 --- /dev/null +++ b/exercise3/.ctests @@ -0,0 +1,4 @@ +task1 +task2 +task3_cpp +task3_py diff --git a/exercise3/.expected-files b/exercise3/.expected-files new file mode 100644 index 0000000..5ee7fd0 --- /dev/null +++ b/exercise3/.expected-files @@ -0,0 +1,4 @@ +task1.main.cpp +task2.cpp +task3.cpp +task3.py diff --git a/exercise3/.gitattributes b/exercise3/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise3/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise3/.gitignore b/exercise3/.gitignore new file mode 100644 index 0000000..efff150 --- /dev/null +++ b/exercise3/.gitignore @@ -0,0 +1,360 @@ +# custom + +*.csv +*.png +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/exercise3/.gitmodules b/exercise3/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/exercise3/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/exercise3/CMakeLists.txt b/exercise3/CMakeLists.txt new file mode 100644 index 0000000..82e8aa3 --- /dev/null +++ b/exercise3/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise3 LANGUAGES CXX + DESCRIPTION "exercise3" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise3") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.cpp) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "45") + + add_executable(task2 task2.cpp task2.test.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3_cpp task2.cpp task3.cpp task3.test.cpp) + add_test(NAME task3_cpp COMMAND task3_cpp WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + find_package(Python3 COMPONENTS Interpreter REQUIRED) + add_test(NAME task3_py COMMAND ${Python3_EXECUTABLE} task3.test.py WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_tests_properties(task3_py PROPERTIES DEPENDS task3_cpp) + + add_custom_target(task3) + add_dependencies(task3 task3_cpp) + diff --git a/exercise3/README.md b/exercise3/README.md new file mode 100644 index 0000000..88125c6 --- /dev/null +++ b/exercise3/README.md @@ -0,0 +1,29 @@ +# Hausübung 3 (3 Punkte) + +**Ausgabe**: Donnerstag 21. März 2024, vormittags. + +**Abgabe bis**: Montag 15. April 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise3`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Vektoren von Vektoren, hier beschränkt auf folgende Typen: + ```cpp + std::vector> + std::vector> + ``` + +- Uebergabe von aufrufbaren Objekten, mittels `std::function` hier beschränkt auf folgenden Typ: + ```cpp + std::function + ``` + +- Numerische Integration und Differenzierung +- Einbinden und Nutzung einer [lokalen Bibilothek](https://sgit.iue.tuwien.ac.at/360050/modules/src/branch/main/iue-io/csv.hpp) zum Schreiben von `.csv`-Dateien sowie Plotten der geschrieben Daten mit Python/Matplotlib. + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise3/compile_flags.txt b/exercise3/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise3/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise3/main.ipynb b/exercise3/main.ipynb new file mode 100644 index 0000000..0872219 --- /dev/null +++ b/exercise3/main.ipynb @@ -0,0 +1,248 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C++-Programm (*vector of vectors*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::cout, std::endl\n", + "\t#include <...>\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion, z.B.:\n", + "\t```cpp\n", + "\tint sum(...){\n", + "\t ...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion (Einstiegspunkt für jedes lauffähige Programm), die Ihre selbest geschriebene Funktion verwendet und die berechneten Ergebnisse in der Konsole ausgibt, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\n", + "\t auto res = sum(...)\t\n", + "\t std::cout << res << std::endl;\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Mathematische Funktionen abtasten, numerische Integration und Differenzierung (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sie implementieren Funktionen, die\n", + "\n", + "- ein Intervall $[a,b]$ mittels $N$ Stellen gleichabständig abstasten -> $\\mathbf{x} = \\left[ x_1, x_2, ... , x_N \\right]$,\n", + "- eine Funktion $f(x)$ für die diskreten Werte im Intervall evaluieren -> $\\mathbf{y} = \\left[ f(x_1), f(x_2), ... , f(x_N) \\right]$,\n", + "- anhand der diskreten Wertepaare ($\\mathbf{x}, \\mathbf{y}$) die Ableitung approximieren:\n", + "\t- Vorwärts-Differenz an der ersten Stelle: $f'(x_1) \\approx \\frac{y_2 - y_1}{x_2 - x_1}$,\n", + "\t- Rückwarts-Differenz an der letzten Stelle: $f'(x_N) \\approx \\frac{y_N - y_{N-1}}{x_N - x_{N-1}}$,\n", + "\t- Zentrale-Differenz für alle anderen Stellen $f'(x_i) \\approx \\frac{y_{i+1} - y_{i-1} }{x_{i+1} - x_{i-1}}$, und\n", + "- anhand der diskreten Wertepaare ($\\mathbf{x}, \\mathbf{y}$) die Stammfunktion approximieren:\n", + "\t- Integrationskonstante an der ersten Stelle: $F(x_1) = C$\n", + "\t- Trapezregel für alle anderen Stellen: $F(x_i) \\approx C + \\sum_{2}^{i} \\left[ 0.5 \\cdot \\left(y_{i}+y_{i-1}\\right) \\cdot \\left(x_i - x_{i-1}\\right) \\right]$." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie die folgenden vier Funktionen:\n", + "\n", + "```cpp\n", + "using Vector = std::vector;\n", + "using Callable = std::function;\n", + "\n", + "Vector range(double start, double end, unsigned int N);\n", + "Vector sample(Vector values, Callable func);\n", + "Vector numdiff(Vector x, Vector y);\n", + "Vector numint(Vector x, Vector y, double C);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklarationen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.hpp`](task2.hpp)\n", + "- Ihre Implementierung erfolgt in [`task2.cpp`](task2.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Diskrete Funktionswerte abspeichern und plotten (1 Punkt)\n", + "\n", + "Sie implementieren eine Funktion in C++ die eine `.csv`-Datei mit diskreten Funktionswerten erzeugt:\n", + "- verwenden Sie Ihre in Aufgabe 2 entwickelten Funktionen zum Abtasten und numerisch Integrieren/Differenzieren\n", + "- verwenden Sie die bereitgestellte Funktion [`iue::io::savetxt`](https://sgit.iue.tuwien.ac.at/360050/modules/src/commit/7b31b845bf2d1297553a8565ba6ca2474305394a/iue-io/csv.hpp#L20) zum Schreiben der `.csv`-Datei\n", + "\n", + "Ebenso implementieren Sie eine Funktion in Python, um die Funktionswerten in der von Ihnen erzeugten `.csv`-Datei zu plotten:\n", + "- verwenden Sie [numpy.loadtxt](https://numpy.org/doc/stable/reference/generated/numpy.loadtxt.html) zum Lesen der `.csv`-Datei\n", + "- verwenden Sie [`Matplotlib`](https://matplotlib.org/stable/tutorials/pyplot.html) zum Plotten der eingelesenen Daten" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie folgende Funktion (**C++**):\n", + "\n", + "```cpp\n", + "\n", + "using Filename = std::filesystem::path;\n", + "using Callable = std::function;\n", + "\n", + "void sample_to_csv(Filename filepath, \n", + " char del, \n", + " char comments, \n", + " Callable func, \n", + " double start, \n", + " double end, \n", + " unsigned int N);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie zudem folgende Funktion (**Python**):\n", + "\n", + "```py\n", + "def plot_discrete_function(csvfile, delimiter, comments, pngfile):\n", + "\tpass # todo: implement\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Ihre Implementierung erfolgt in [`task3.py`](task3.py)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.py`](task3.test.py)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die final erzeugten Plots könnten z.B. so aussehen:\n", + "\n", + "![images/task3_plot_sin.png](images/task3_plot_sin.png) \n", + "![images/test3_plot_cos.png](images/task3_plot_cos.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `g++` und `python`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "g++ -g -std=c++20 task1.main.cpp -o build/task1.exe\n", + "g++ -g -std=c++20 task2.cpp task2.test.cpp -o build/task2.exe\n", + "g++ -g -Imodules -std=c++20 task2.cpp task3.cpp task3.test.cpp -o build/task3.exe\n", + "\n", + "# run tests\n", + "./build/task1.exe\n", + "./build/task2.exe\n", + "./build/task3.exe\n", + "python task3.test.py\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):s\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 \n", + "ctest --test-dir build -C Debug -R task2 \n", + "ctest --test-dir build -C Debug -R task3 \n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise3/modules b/exercise3/modules new file mode 160000 index 0000000..b8ce24c --- /dev/null +++ b/exercise3/modules @@ -0,0 +1 @@ +Subproject commit b8ce24c87fc76396de465b2573e19909fd10cf13 diff --git a/exercise3/task1.main.cpp b/exercise3/task1.main.cpp new file mode 100644 index 0000000..f7f66c5 --- /dev/null +++ b/exercise3/task1.main.cpp @@ -0,0 +1,38 @@ +/// @file +/// @brief Task1: "single-file" excutable C++ program + +#include +#include + +/// @brief Calculate the sum of all integer values in a std::vector> +/// @param data A vector of vectors containing the values to be summed +/// @return Sum of all values in data +double sum(const std::vector>& data) { + double sum = 0; + for (const auto& row : data) { + for (const auto& value : row) { + sum += value; + } + } + return sum; +} + +/// @brief main function (entry point for executable) conducting the following tasks in this order +/// - create and prepare a local variable of type std::vector> +/// holding 9 int values in this arrangement: +/// 1, 2, 3 +/// 4, 5, 6 +/// 7, 8, 9 +/// - call your function 'sum' and provide the prepared variable as argument to the call +/// - capture the result of your function call in a local variable and print it to the console +int main() { + std::vector> data = { + {1, 2, 3}, + {4, 5, 6}, + {7, 8, 9} + }; + double result = sum(data); + std::cout << "Sum: " << result << std::endl; + return 0; +} + diff --git a/exercise3/task2.cpp b/exercise3/task2.cpp new file mode 100644 index 0000000..5670606 --- /dev/null +++ b/exercise3/task2.cpp @@ -0,0 +1,71 @@ +/// @file +/// @brief Task2: implementation + +#include "task2.hpp" + +/// @todo Include standard library headers as needed +#include // assert +#include // std::function +#include // std::vector +#include // std::abs + +/// @brief Creates a sequence of equidistant values in a given interval (inclusive). +/// @param start Start of the interval +/// @param end End of the interval +/// @param N Number of values; assumption: N >= 2 +/// @return Sequence of equidistant values in increasing order +std::vector range(double start, double end, unsigned int N) { + assert(N >= 2); + std::vector values(N); + double step = (end - start) / (N - 1); + for (unsigned int i = 0; i < N; ++i) { + values[i] = start + i * step; + } + return values; +} + +/// @brief Evaluates a one-dimensional scalar function at the provided discrete locations +/// @param values Sequence of discrete locations +/// @param func Callable with a signature compatible with f(double) -> double +/// @return Sequence of function values +std::vector sample(std::vector values, std::function func) { + std::vector results(values.size()); + for (unsigned int i = 0; i < values.size(); ++i) { + results[i] = func(values[i]); + } + return results; +} + +/// @brief Performs a numerical differentiation using a combined forward/center/backward difference scheme +/// @param x Discrete sequence of locations; assumption: two or more values, ascending, and equally spaced +/// @param y Discrete sequence of function values; assumption: same size as 'x' +/// @return Sequence of function values of the numerical derivative +std::vector numdiff(std::vector x, std::vector y) { + assert(x.size() == y.size()); + assert(x.size() >= 2); + std::vector derivative(x.size()); + double h = x[1] - x[0]; + derivative[0] = (y[1] - y[0]) / h; + for (unsigned int i = 1; i < x.size() - 1; ++i) { + derivative[i] = (y[i + 1] - y[i - 1]) / (2 * h); + } + derivative[x.size() - 1] = (y[x.size() - 1] - y[x.size() - 2]) / h; + return derivative; +} + +/// @brief Performs a numerical integration using the trapezoidal rule +/// @param x Discrete sequence of locations; assumption: two or more values, ascending, and equally spaced +/// @param y Discrete sequence of function values; assumption: same size as 'x' +/// @param C Constant of integration +/// @return Sequence of function values of the numerical antiderivative +std::vector numint(std::vector x, std::vector y, double C) { + assert(x.size() == y.size()); + assert(x.size() >= 2); + std::vector integral(x.size()); + double h = x[1] - x[0]; + integral[0] = C; + for (unsigned int i = 1; i < x.size(); ++i) { + integral[i] = integral[i - 1] + (y[i - 1] + y[i]) * h / 2; + } + return integral; +} diff --git a/exercise3/task2.hpp b/exercise3/task2.hpp new file mode 100644 index 0000000..4a42f86 --- /dev/null +++ b/exercise3/task2.hpp @@ -0,0 +1,34 @@ +/// @file +/// @brief Task2: function declarations + +#pragma once + +#include // std::function +#include // std::vector + +/// @brief Creates a sequence of equidistant values in a given interval (inclusive). +/// @param start Start of the interval +/// @param end End of the interval +/// @param N Number of values; assumption: N >= 2 +/// @return Sequence of equidistant values in increasing order +std::vector range(double start, double end, unsigned int N); + +/// @brief Evaluates a one-dimensional scalar function at the provided discrete locations +/// @param values Sequence of discrete locations +/// @param func Callable with a signature compatible with f(double) -> double +/// @return Sequence of function values +std::vector sample(std::vector values, std::function func); + +/// @brief Performs a numerical differentiation using a combined forward/center/backward difference scheme +/// @param x Discrete sequence of locations; assumption: two or more values, ascending, and equally spaced +/// @param y Discrete sequence of function values; assumption: same size as 'x' +/// @return Sequence of function values of the numerical derivative +std::vector numdiff(std::vector x, std::vector y); + +/// @brief Performs a numerical integration using the trapezoidal rule +/// @param x Discrete sequence of locations; assumption: two or more values, ascending, and equally spaced +/// @param y Discrete sequence of function values; assumption: same size as 'x' +/// @param C Constant of integration +/// @return Sequence of function values of the numerical antiderivative +std::vector numint(std::vector x, std::vector y, double C); + diff --git a/exercise3/task2.test.cpp b/exercise3/task2.test.cpp new file mode 100644 index 0000000..d9758fb --- /dev/null +++ b/exercise3/task2.test.cpp @@ -0,0 +1,97 @@ +/// @file +/// @brief Task2: tests + +#include "task2.hpp" + +#include // assert +#include // std::abs|sin +#include // std::cout|endl +#include // std::vector + +namespace help { + +/// @brief only to disambiguate the 'sin'-overloads from cmath +double sin(double value) { return std::sin(value); } + +/// @brief only to disambiguate the 'cos'-overloads from cmath +double cos(double value) { return std::cos(value); } + +} // namespace help + +int main() { + + { // testing function 'range' + std::vector x = range(0, 10, 11); + + double dx = x[1] - x[0]; + assert(std::abs(dx - 1.0) < 1e-7); + for (unsigned int i = 1; i != x.size(); ++i) + assert(std::abs(x[i] - x[i - 1] - dx) < 1e-7); + } + + { // testing functions 'range' and 'sample' + std::vector x = range(2, 10, 6); + std::vector f = sample(x, help::sin); + for (unsigned int i = 0; i != x.size(); ++i) + assert(std::abs(f[i] - help::sin(x[i])) < 1e-7); + } + + { // testing functions 'range' and 'sample' + std::vector x = range(0, 5, 6); + std::vector f = sample(x, help::cos); + for (unsigned int i = 0; i != x.size(); ++i) + assert(std::abs(f[i] - help::cos(x[i])) < 1e-7); + } + + { // testing function 'numdiff' + std::vector x = {0, 1, 2, 3, 4}; + std::vector f = {0, 1, 1, 1, 0.5}; + std::vector df = numdiff(x, f); + assert(std::abs(df[0] - 1.0) < 1e-7); + assert(std::abs(df[1] - 0.5) < 1e-7); + assert(std::abs(df[2] - 0.0) < 1e-7); + assert(std::abs(df[3] + 0.25) < 1e-7); + assert(std::abs(df[4] + 0.5) < 1e-7); + } + + { // testing function 'numdiff' + double s = 2.0; + std::vector x = {0*s, 1*s, 2*s, 3*s, 4*s}; + std::vector f = {0, 1, 1, 1, 0.5}; + std::vector df = numdiff(x, f); + assert(std::abs(df[0] - 1.00/s) < 1e-7); + assert(std::abs(df[1] - 0.50/s) < 1e-7); + assert(std::abs(df[2] - 0.00/s) < 1e-7); + assert(std::abs(df[3] + 0.25/s) < 1e-7); + assert(std::abs(df[4] + 0.50/s) < 1e-7); + } + + + { // testing function 'numint' + std::vector x = {0, 1, 2, 3, 4}; + std::vector f = {0, 1, 1, 1, 0}; + std::vector F = numint(x, f, 0.0); + assert(std::abs(F[0] - 0.0) < 1e-7); + assert(std::abs(F[1] - (F[0] + 0.5)) < 1e-7); + assert(std::abs(F[2] - (F[1] + 1.0)) < 1e-7); + assert(std::abs(F[3] - (F[2] + 1.0)) < 1e-7); + assert(std::abs(F[4] - (F[3] + 0.5)) < 1e-7); + } + + { // testing function 'numint' + double s = 2.0; + std::vector x = {0*s, 1*s, 2*s, 3*s, 4*s, 5*s}; + std::vector f = {0, 1, 1, 1, 0, -1}; + std::vector F = numint(x, f, 10.0); + assert(std::abs(F[0] - 10.0) < 1e-7); + assert(std::abs(F[1] - (F[0] + 0.5*s)) < 1e-7); + assert(std::abs(F[2] - (F[1] + 1.0*s)) < 1e-7); + assert(std::abs(F[3] - (F[2] + 1.0*s)) < 1e-7); + assert(std::abs(F[4] - (F[3] + 0.5*s)) < 1e-7); + assert(std::abs(F[5] - (F[4] - 0.5*s)) < 1e-7); + } + + std::cout << "task2.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise3/task3.cpp b/exercise3/task3.cpp new file mode 100644 index 0000000..a874163 --- /dev/null +++ b/exercise3/task3.cpp @@ -0,0 +1,45 @@ +/// @file +/// @brief Task3: implementation + +#include "task3.hpp" // sample_to_csv +#include "iue-io/csv.hpp" // iue::io::savetxt +#include "task2.hpp" // range|sample|numint|numdiff + +/// @todo Include standard library headers as needed +#include // std::function +#include // std::vector + +/// @brief This function +/// - samples a one-dimensional scalar function (f) in a provided interval and resolution, +// - approximates its derivative (df) and antiderivative (F) numerically, and +/// - produces a csv-file holding the discrete values in this form: +/// - csv-column layout: x, f, F, df +/// @param filepath +/// @param del Delimiter +/// @param comment Character designating a line as a comment +/// @param func Callable with a signature compatible with f(double) -> double +/// @param start Start of the interval +/// @param end End of the interval +/// @param N Number of values; assumption: N >= 2 +void sample_to_csv(std::filesystem::path filepath, char del, char comments, std::function func, + double start, double end, unsigned int N) { + // Create a sequence of equidistant values in the interval [start, end] + std::vector x = range(start, end, N); + // Sample the function 'func' at the locations 'x' + std::vector f = sample(x, func); + // Approximate the derivative of 'f' at the locations 'x' + std::vector df = numdiff(x, f); + // Approximate the antiderivative of 'f' at the locations 'x' + std::vector F = numint(x, f, 0.0); + // Create a matrix holding the discrete values of 'x', 'f', 'F', and 'df' + std::vector> data(N, std::vector(4)); + for (unsigned int i = 0; i < N; ++i) { + data[i][0] = x[i]; + data[i][1] = f[i]; + data[i][2] = F[i]; + data[i][3] = df[i]; + } + // Save the matrix to a csv-file + iue::io::savetxt(filepath, data, del); + +} diff --git a/exercise3/task3.hpp b/exercise3/task3.hpp new file mode 100644 index 0000000..08ecaf7 --- /dev/null +++ b/exercise3/task3.hpp @@ -0,0 +1,22 @@ +/// @file +/// @brief Task3: function declarations + +#pragma once + +#include // std::filesystem::path +#include // std::function + +/// @brief This function +/// - samples a one-dimensional scalar function (f) in a provided interval and resolution, +// - approximates its derivative (df) and antiderivative (F) numerically, and +/// - produces a csv-file holding the discrete values in this form: +/// - csv-column layout: x, f, F, df +/// @param filepath +/// @param del Delimiter +/// @param comment Character designating a line as a comment +/// @param func Callable with a signature compatible with f(double) -> double +/// @param start Start of the interval +/// @param end End of the interval +/// @param N Number of values; assumption: N >= 2 +void sample_to_csv(std::filesystem::path filepath, char del, char comments, std::function func, + double start, double end, unsigned int N); diff --git a/exercise3/task3.py b/exercise3/task3.py new file mode 100644 index 0000000..bd754c9 --- /dev/null +++ b/exercise3/task3.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +""" Task3: implementation """ + +import numpy as np +import matplotlib.pyplot as plt + +def plot_discrete_function(csvfile, delimiter, comments, pngfile): + """ + Reads a csv-file containing discretized value of a function (f), its derivative (df) and antiderivative (F) + and plots all three functions over the discrete value of the interval (x). + + Expected csv-column layout: + x, f, F, df + + Requirements for the plot: + - axis labels + - a legend for the plotted data records + + Implementation hints: + - use numpy.loadtxt(...) for loading the data from the csvfile + - use Matplotlib for plotting + + Parameters + ---------- + csvfile : string + Name of the csv-file containing the discrete function + delimiter: character + Charater used to delimit individual values in the rows + comments: character + Charater (when used as first character in a row) denoting comment lines + pngfile : string + Name of the file where the plot is saved + """ + # load data from csv file + data = np.loadtxt(csvfile, delimiter=delimiter, comments=comments) + x = data[:, 0] + f = data[:, 1] + F = data[:, 2] + df = data[:, 3] + + # plot the data + plt.plot(x, f, label='f') + plt.plot(x, F, label='F') + plt.plot(x, df, label='df') + plt.xlabel('x') + plt.ylabel('y') + plt.legend() + plt.savefig(pngfile) + plt.close('all') diff --git a/exercise3/task3.test.cpp b/exercise3/task3.test.cpp new file mode 100644 index 0000000..7c43b88 --- /dev/null +++ b/exercise3/task3.test.cpp @@ -0,0 +1,59 @@ +/// @file +/// @brief Task3: tests + +#include "task3.hpp" + +#include "iue-io/csv.hpp" + +#include // assert +#include // std::sin +#include // std::filesystem::remove +#include // std::cout|endl +#include // std::numbers::pi + +int main() { + + { + auto f = [](double x) { return cos(x); }; + auto df = [](double x) { return -sin(x); }; + auto F = [](double x) { return sin(x) - (sin(0)); }; + std::filesystem::path filename = "test.task3.cos.csv"; + + std::filesystem::remove(filename); + sample_to_csv(filename, ';', '#', f, 0, 2 * std::numbers::pi, 18); + auto table = iue::io::loadtxt(filename, ';', '#'); + + assert(table.size() == 18); + + for (unsigned int r = 0; r != table.size(); ++r) { + assert(table[r].size() == 4); + assert(std::abs(f(table[r][0]) - table[r][1]) < 0.2); + assert(std::abs(F(table[r][0]) - table[r][2]) < 0.2); + assert(std::abs(df(table[r][0]) - table[r][3]) < 0.2); + } + } + + { + auto f = [](double x) { return sin(x); }; + auto df = [](double x) { return cos(x); }; + auto F = [](double x) { return -cos(x) - (-cos(0)); }; + std::filesystem::path filename = "test.task3.sin.csv"; + + std::filesystem::remove(filename); + sample_to_csv(filename, ';', '#', f, 0, 2 * std::numbers::pi, 360); + auto table = iue::io::loadtxt(filename, ';', '#'); + + assert(table.size() == 360); + + for (unsigned int r = 0; r != table.size(); ++r) { + assert(table[r].size() == 4); + assert(std::abs(f(table[r][0]) - table[r][1]) < 0.01); + assert(std::abs(F(table[r][0]) - table[r][2]) < 0.01); + assert(std::abs(df(table[r][0]) - table[r][3]) < 0.01); + } + } + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise3/task3.test.py b/exercise3/task3.test.py new file mode 100644 index 0000000..05fe867 --- /dev/null +++ b/exercise3/task3.test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +""" Task3: tests """ + +import os +import unittest +import matplotlib.pyplot as plt +import task3 + + +class Test(unittest.TestCase): + def test_plot_sin(self): + + figname = "test.task3.sin.png" + + if os.path.isfile(figname): + os.remove(figname) + + plt.close('all') + task3.plot_discrete_function("test.task3.sin.csv",";","#",figname) + plt.close('all') + + self.assertTrue(os.path.isfile(figname)) + + def test_plot_cos(self): + + figname = "test.task3.cos.png" + + if os.path.isfile(figname): + os.remove(figname) + + plt.close('all') + task3.plot_discrete_function("test.task3.cos.csv",";","#",figname) + plt.close('all') + + self.assertTrue(os.path.isfile(figname)) + +if __name__ == "__main__": + unittest.main() diff --git a/exercise4/.clang-format b/exercise4/.clang-format new file mode 100644 index 0000000..0c35b1b --- /dev/null +++ b/exercise4/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise4/.clangd b/exercise4/.clangd new file mode 100644 index 0000000..68a6e4c --- /dev/null +++ b/exercise4/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: +# - --target=x86_64-w64-windows-gnu +# - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/exercise4/.ctests b/exercise4/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise4/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise4/.expected-files b/exercise4/.expected-files new file mode 100644 index 0000000..7ec8475 --- /dev/null +++ b/exercise4/.expected-files @@ -0,0 +1,3 @@ +task1.main.cpp +task2.cpp +task3.cpp diff --git a/exercise4/.gitattributes b/exercise4/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise4/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise4/.gitignore b/exercise4/.gitignore new file mode 100644 index 0000000..efff150 --- /dev/null +++ b/exercise4/.gitignore @@ -0,0 +1,360 @@ +# custom + +*.csv +*.png +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/exercise4/.gitmodules b/exercise4/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/exercise4/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/exercise4/CMakeLists.txt b/exercise4/CMakeLists.txt new file mode 100644 index 0000000..2f4d995 --- /dev/null +++ b/exercise4/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise4 LANGUAGES CXX + DESCRIPTION "exercise4" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise4") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.cpp) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "(-112|-1\\.120000e\\+02)") + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "(-221|-2\\.110000e\\+02)") + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "(42|4\\.200000e\\+01)") + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "(-23|-2\\.300000e\\+01)") + + add_executable(task2 task2.cpp task2.misc.cpp task2.test.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3 task2.cpp task2.misc.cpp task3.cpp task3.misc.cpp task3.test.cpp) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + diff --git a/exercise4/README.md b/exercise4/README.md new file mode 100644 index 0000000..c2d8b9a --- /dev/null +++ b/exercise4/README.md @@ -0,0 +1,19 @@ +# Hausübung 4 (3 Punkte) + +**Ausgabe**: Dienstag 16. April 2024, vormittags (Ursprünglich Donnerstag 11. April 2024). + +**Abgabe bis**: Montag 29. April 2024, Ende des Tages. (Ursprünglich Montag 22. April) + +**Abgabe via**: git-Repository mit dem Namen **`exercise4`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Klassen mit ausschließlich öffentlichen Member Variablen (ohne benutzerdefinierte Konstruktoren) +- Member Funktionen +- Nutzung von selbst implementierter Funktionalität in einem separaten Anwendungskontext + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise4/compile_flags.txt b/exercise4/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise4/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise4/images/dusty_nikolaus_city.svg b/exercise4/images/dusty_nikolaus_city.svg new file mode 100644 index 0000000..7fd4cf4 --- /dev/null +++ b/exercise4/images/dusty_nikolaus_city.svg @@ -0,0 +1,1145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exercise4/images/trains.svg b/exercise4/images/trains.svg new file mode 100644 index 0000000..86c1b75 --- /dev/null +++ b/exercise4/images/trains.svg @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exercise4/main.ipynb b/exercise4/main.ipynb new file mode 100644 index 0000000..94cfb54 --- /dev/null +++ b/exercise4/main.ipynb @@ -0,0 +1,256 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C++-Programm (*coordinate rotation*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::array\n", + "\t#include // std::sin, std::cos\n", + "\t#include // std::cout, std::endl\n", + "\t#include // std::numbers::pi\n", + "\t...\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion in einem eignene Namensraum, z.B.:\n", + "\t```cpp\n", + "\tnamespace task1 {\n", + "\n", + "\tusing Coord = std::array;\n", + "\n", + "\tCoord rotate_counter_clockwise(Coord coord, double angle) {\n", + "\t\t...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet und die berechneten Ergebnisse in der Konsole ausgibt, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\n", + "\t auto [xrot, yrot] = rotate_counter_clockwise(...)\t\n", + "\t std::cout << xrot << \" \" << yrot << std::endl;\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Member Funktionen und Klassen-Invarianten (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gegeben ist die Definition dreier Klassen `BBox`, `Cirlce` und `Triangle`. Die Klassen sind sog. `aggregate`-Klassen und weisen u.A. folgende Eigenschaften auf:\n", + "\n", + "- Ausschließlich öffentliche Member-Variablen\n", + "- Keine benutzerdefinierten Konstruktoren\n", + "- Parameterlose Konstruktion möglich, z.B. `Aggregate aggregate = {};`\n", + "- Listen-Initialisierung ist möglich, z.B. `Aggregate aggregate = { value1, valu2 };`\n", + "- Struktuierte Zuweisung ist möglich, z.B. `const auto& [m1, m2] = aggregate;`\n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie die folgenden Member-Funktionen:\n", + "\n", + "```cpp\n", + "namespace task2 {\n", + "\n", + "using Vec2d = std::array;\n", + "\n", + "/// @brief Axis-aligned bounding box\n", + "struct BBox {\n", + " Vec2d min; ///< coord of bottom left corner\n", + " Vec2d max; ///< coord of top right corner\n", + " BBox scale(const Vec2d& org, double s) const; // todo\n", + " BBox translate(const Vec2d& offset) const; // todo\n", + " bool check_invariants() const; // todo\n", + "};\n", + "\n", + "/// @brief Circle\n", + "struct Circle {\n", + " Vec2d c; ///< coordinate of the center of the circle\n", + " double r; ///< radius of the circle\n", + " BBox bbox() const; // todo\n", + " Circle scale(const Vec2d& org, double s) const; // todo\n", + " Circle rotate(const Vec2d& org, double angle) const; // todo\n", + " Circle translate(const Vec2d& offset) const; // todo\n", + " bool check_invariants() const; // todo\n", + "};\n", + "\n", + "/// @brief Triangle\n", + "struct Triangle {\n", + " std::array abc; ///< three corner points of the triangle\n", + " BBox bbox() const; // todo\n", + " Triangle scale(const Vec2d& org, double s) const; // todo\n", + " Triangle rotate(const Vec2d& org, double angle) const;// todo\n", + " Triangle translate(const Vec2d& offset) const; // todo\n", + " bool check_invariants() const; // todo\n", + "};\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklarationen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.hpp`](task2.hpp)\n", + "- Ihre Implementierung erfolgt in [`task2.cpp`](task2.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp)\n", + "- In [`task2.misc.hpp`](task2.misc.hpp)/[`task2.misc.cpp`](task2.misc.cpp) sind Hilfsfunktionen zum Ausgeben und Vergleichen der drei Klassen gegeben (diese werden bei den Tests verwendet, stehen Ihnen aber auch für Ihre Implementierung zur Verfügung).\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Nutzung von bereitgestellter Funktionalität sowie der Implementierung aus Aufgabe 2 zum Erstellen einer eigenen `.svg`-Grafik (1 Punkt)\n", + "\n", + "Sie sollten die Funktionalität, die Sie in Aufgabe 2 implementiert haben (Skalieren/Verschieben/Rotieren) nun selbst nutzen um eine `.svg`-Grafik zu erstellen.\n", + "\n", + "Nutzen Sie die bereitgestellte Funktion `task3::render_wrapper` um die `.svg`-Grafik zu generieren: Sie können die Typen aus Aufgabe 2 (`BBox/Circle/Triangle`) direkt übergeben:\n", + "\n", + "```cpp\n", + "// std::vector boxes = [ ... ]\n", + "// std::vector circles = [ ... ]\n", + "// std::vector triangles = [ ... ]\n", + "task3::render_wrapper(\"myimage.svg\", boxes, triangles, triangles);\n", + "```\n", + "\n", + "Es gibt keine Vorgaben, lediglich, dass die erstellte Grafik mindestens 20 Elemente (Boxen/Kreise/Dreiecke) enthalten muss. \n", + "\n", + "Hier ein Beispiel wie so etwas aussehen kann \n", + "\n", + "- horizontal verschobene und skalierte \"Häuser\" mit \"Himmel\" aus zufällig angeordneten Kreisen:\n", + "\n", + "\t![images/dusty_nikolaus_city.svg](images/dusty_nikolaus_city.svg)\n", + "\n", + "- horizontal verschobene \"Lokomotiven\"\n", + "\n", + "\t![images/trains.svg](images/trains.svg)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie folgende Funktion:\n", + "\n", + "```cpp\n", + "\n", + "namespace task3 {\n", + "\n", + "int render_something(std::filesystem::path filepath); // todo\n", + "\n", + "}\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n", + "- In [`task3.misc.hpp`](task3.misc.hpp)/[`task3.misc.cpp`](task3.misc.cpp) finden Sie die oben erwähnte Hilfsfunktion `task3::render_wrapper`.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `g++` und `python`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "g++ -g -std=c++20 task1.main.cpp -o build/task1\n", + "g++ -g -Imodules -std=c++20 task2.cpp task2.misc.cpp task2.test.cpp -o build/task2\n", + "g++ -g -Imodules -std=c++20 task2.cpp task2.misc.cpp task3.cpp task3.misc.cpp task3.test.cpp -o build/task3\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\n", + "./build/task3\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):s\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 \n", + "ctest --test-dir build -C Debug -R task2 \n", + "ctest --test-dir build -C Debug -R task3 \n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise4/modules b/exercise4/modules new file mode 160000 index 0000000..df348de --- /dev/null +++ b/exercise4/modules @@ -0,0 +1 @@ +Subproject commit df348de23a73810279468501510b754bc622797f diff --git a/exercise4/task1.main.cpp b/exercise4/task1.main.cpp new file mode 100644 index 0000000..05df3c9 --- /dev/null +++ b/exercise4/task1.main.cpp @@ -0,0 +1,43 @@ +/// @file +/// @brief Task1: "single-file" excutable C++ program + +/// @todo Include standard library headers as needed +#include // std::array +#include +#include + +namespace task1 { + +using Coord = std::array; + +/// @todo Implement a function 'rotate_counter_clockwise' according to the description below: +/// - the function receives a two-dimensional coordinate in form of a 'std::array' +/// - the function receives a rotation angle (in radians) in form of a 'double' +/// - the function rotates the coordinate counter clockwise around the origin +/// - the function returns the rotated coordinate as a std::array + +std::array rotate_counter_clockwise(const Coord& coord, double angle) { + std::array rotated_coord = {0, 0}; + rotated_coord[0] = coord[0] * cos(angle) - coord[1] * sin(angle); + rotated_coord[1] = coord[0] * sin(angle) + coord[1] * cos(angle); + return rotated_coord; +} + +} // namespace task1 + +/// @todo Implement a main function conducting the following tasks in this order: +/// - Create two coordinates (local variables): +/// - std::array coord1 = {112,211}; +/// - std::array coord2 = {-42,23}; +/// - Use your 'rotate_counter_clockwise' function to rotate both coordinate by 180 degrees +/// - Print the resulting rotated coordinates to the console +/// - Hint: the expected coordinates after rotating 180 degrees are +/// - std::array coord1_rotated = {-112,-211}; +/// - std::array coord2_rotated = {42,-23}; +int main() { + std::array coord1 = {112, 211}; + std::cout << "Original coord1: " << coord1[0] << ", " << coord1[1] << std::endl; + std::array coord1_rotated = task1::rotate_counter_clockwise(coord1, std::numbers::pi_v); + std::cout << "Rotated coord1: " << coord1_rotated[0] << ", " << coord1_rotated[1] << std::endl; + return 0; +} \ No newline at end of file diff --git a/exercise4/task2.cpp b/exercise4/task2.cpp new file mode 100644 index 0000000..784443a --- /dev/null +++ b/exercise4/task2.cpp @@ -0,0 +1,171 @@ +/// @file +/// @brief Task2: member function definitions/implementations + +#include "task2.hpp" // task2::Vec2, task2::BBox, task2::Circle, task2::Triangle +#include "task2.misc.hpp" // task2::operator<<, task2::isnan, task2::isclose + +#include "modules/iue-num/numerics.hpp" // iue::num::isclose + +/// @todo Include standard library headers as needed + + +namespace task2 { + +/// ==================================== Bounding Box ================================================= +/// @todo Implement the missing member functions for bounding box as declared and specified in task2.hpp + +/// @todo implement member function 'BBox::scale' +BBox BBox::scale(const Vec2d& org, double s) const { + BBox scaled_box = *this; + scaled_box.min[0] = org[0] + s * (min[0] - org[0]); + scaled_box.min[1] = org[1] + s * (min[1] - org[1]); + scaled_box.max[0] = org[0] + s * (max[0] - org[0]); + scaled_box.max[1] = org[1] + s * (max[1] - org[1]); + return scaled_box; +} + +/// @todo implement member function 'BBox::translate' +BBox BBox::translate(const Vec2d& offset) const { + BBox translated_box = *this; + translated_box.min[0] += offset[0]; + translated_box.min[1] += offset[1]; + translated_box.max[0] += offset[0]; + translated_box.max[1] += offset[1]; + return translated_box; +} + +/// @todo implement member function 'BBox::check_invariants' +bool BBox::check_invariants() const { + if (std::isnan(min[0]) || std::isnan(min[1]) || std::isnan(max[0]) || std::isnan(max[1])) { + return false; + } + if (min[0] > max[0] || min[1] > max[1]) { + return false; + } + if (iue::num::isclose(min[0], max[0]) && iue::num::isclose(min[1], max[1])) { + return false; + } + return true; +} + + +/// ==================================== Circle ================================================ +/// @todo Implement the missing member functions for Circle as declared and specified in task2.hpp + +/// @todo implement member function 'Circle::bbox' +BBox Circle::bbox() const { + BBox box = {{c[0] - r, c[1] - r}, {c[0] + r, c[1] + r}}; + return box; +} + +/// @todo implement member function 'Circle::scale' +Circle Circle::scale(const Vec2d& org, double s) const { + Circle scaled_circle = *this; + scaled_circle.c[0] = org[0] + s * (c[0] - org[0]); + scaled_circle.c[1] = org[1] + s * (c[1] - org[1]); + scaled_circle.r *= s; + return scaled_circle; +} + +/// @todo implement member function 'Circle::rotate' +Circle Circle::rotate(const Vec2d& org, double angle) const { + Circle rotated_circle = *this; + double x = c[0] - org[0]; + double y = c[1] - org[1]; + rotated_circle.c[0] = org[0] + x * cos(angle) - y * sin(angle); + rotated_circle.c[1] = org[1] + x * sin(angle) + y * cos(angle); + return rotated_circle; +} + +/// @todo implement member function 'Circle::translate' +Circle Circle::translate(const Vec2d& offset) const { + Circle translated_circle = *this; + translated_circle.c[0] += offset[0]; + translated_circle.c[1] += offset[1]; + return translated_circle; +} + +/// @todo implement member function 'Circle::check_invariants' +bool Circle::check_invariants() const { + if (std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(r)) { + return false; + } + if (r < 0) { + return false; + } + return true; +} + +/// ==================================== Triangle ================================================ +/// @todo Implement the missing member functions for Triangle as declared and specified in task2.hpp + +/// @todo implement member function 'Triangle::bbox' +BBox Triangle::bbox() const { + BBox box = {{abc[0][0], abc[0][1]}, {abc[0][0], abc[0][1]}}; + for (int i = 1; i < 3; i++) { + if (abc[i][0] < box.min[0]) { + box.min[0] = abc[i][0]; + } + if (abc[i][0] > box.max[0]) { + box.max[0] = abc[i][0]; + } + if (abc[i][1] < box.min[1]) { + box.min[1] = abc[i][1]; + } + if (abc[i][1] > box.max[1]) { + box.max[1] = abc[i][1]; + } + } + return box; +} + +/// @todo implement member function 'Triangle::scale' +Triangle Triangle::scale(const Vec2d& org, double s) const { + Triangle scaled_triangle = *this; + for (int i = 0; i < 3; i++) { + scaled_triangle.abc[i][0] = org[0] + s * (abc[i][0] - org[0]); + scaled_triangle.abc[i][1] = org[1] + s * (abc[i][1] - org[1]); + } + return scaled_triangle; +} + +/// @todo implement member function 'Triangle::rotate' +Triangle Triangle::rotate(const Vec2d& org, double angle) const { + Triangle rotated_triangle = *this; + for (int i = 0; i < 3; i++) { + double x = abc[i][0] - org[0]; + double y = abc[i][1] - org[1]; + rotated_triangle.abc[i][0] = org[0] + x * cos(angle) - y * sin(angle); + rotated_triangle.abc[i][1] = org[1] + x * sin(angle) + y * cos(angle); + } + return rotated_triangle; +} + +/// @todo implement member function 'Triangle::translate' +Triangle Triangle::translate(const Vec2d& offset) const { + Triangle translated_triangle = *this; + for (int i = 0; i < 3; i++) { + translated_triangle.abc[i][0] += offset[0]; + translated_triangle.abc[i][1] += offset[1]; + } + return translated_triangle; +} + +/// @todo implement member function 'Triangle::bbox' +bool Triangle::check_invariants() const { + if (std::isnan(abc[0][0]) || std::isnan(abc[0][1]) || std::isnan(abc[1][0]) || std::isnan(abc[1][1]) || std::isnan(abc[2][0]) || std::isnan(abc[2][1])) { + return false; + } + if (iue::num::isclose(abc[0][0], abc[1][0]) && iue::num::isclose(abc[0][1], abc[1][1])) { + return false; + } + if (iue::num::isclose(abc[0][0], abc[2][0]) && iue::num::isclose(abc[0][1], abc[2][1])) { + return false; + } + if (iue::num::isclose(abc[1][0], abc[2][0]) && iue::num::isclose(abc[1][1], abc[2][1])) { + return false; + } + return true; +} + +} // namespace task2 diff --git a/exercise4/task2.hpp b/exercise4/task2.hpp new file mode 100644 index 0000000..1392d76 --- /dev/null +++ b/exercise4/task2.hpp @@ -0,0 +1,87 @@ +/// @file +/// @brief Task2: class definitions with member function declarations + +#pragma once + +#include // std::array + +namespace task2 { + +using Vec2d = std::array; + +/// @brief Axis-aligned bounding box +struct BBox { + + Vec2d min; ///< coord of bottom left corner + Vec2d max; ///< coord of top right corner + + /// @brief Scales the bounding box (relative to a reference coordinate) + /// @param org Reference coordinate + /// @param s Scaling factor + BBox scale(const Vec2d& org, double s) const; + /// @brief Translates the line + /// @param offset Translation vector + BBox translate(const Vec2d& offset) const; + /// @brief Checks if all invariants are fullfilled + /// @return false if any invariant is violated, true otherwise + /// Invariants: + /// - no coordinate is NAN + /// - the individual coordinates of 'min' are less or equal the respective coordinates in 'max' + /// - 'min' is not close to 'max' (use iue::num::isclose to check) + bool check_invariants() const; +}; + +/// @brief Circle +struct Circle { + + Vec2d c; ///< coordinate of the center of the circle + double r; ///< radius of the circle + + /// @brief Generates the minimum axis-aligned bounding box containing this circle + /// @return axis-aligned bounding box + BBox bbox() const; + /// @brief Scales the circle center (relative to a reference coordinate), and the radius with a scalar value + /// @param org Reference coordinate + /// @param s Scaling factor + Circle scale(const Vec2d& org, double s) const; + /// @brief Rotates the circle center (relative to a reference coordinate) counter clockwise + /// @param org Reference coordinate + /// @param angle Rotation angle (in radians) + Circle rotate(const Vec2d& org, double angle) const; + /// @brief Translates the circle center + /// @param offset Translation vector + Circle translate(const Vec2d& offset) const; + /// @brief Checks if all invariants are fullfilled + /// @return false if any invariant is violated, true otherwise + /// @note Invariants: radius is non-negative and no coordinate is NAN + bool check_invariants() const; +}; + +/// @brief Triangle +struct Triangle { + + std::array abc; ///< three corner points of the triangle + + /// @brief Generates the minimum axis-aligned bounding box containing this triangle + /// @return axis-aligned bounding box + BBox bbox() const; + /// @brief Scales the triangle (relative to a reference coordinate) + /// @param org Reference coordinate + /// @param s Scaling factor + Triangle scale(const Vec2d& org, double s) const; + /// @brief Rotates the triangle (relative to a reference coordinate) counter clockwise + /// @param org Reference coordinate + /// @param angle Rotation angle (in radians) + Triangle rotate(const Vec2d& org, double angle) const; + /// @brief Translates the line + /// @param offset Translation vector + Triangle translate(const Vec2d& offset) const; + /// @brief Checks if all invariants are fullfilled + /// @return false if any invariant is violated, true otherwise + /// Invariants: + /// - no coordinate is NAN + /// - none of the corner points are close to each other (use iue::num::isclose to check) + bool check_invariants() const; +}; + +} // namespace task2 diff --git a/exercise4/task2.misc.cpp b/exercise4/task2.misc.cpp new file mode 100644 index 0000000..acd9670 --- /dev/null +++ b/exercise4/task2.misc.cpp @@ -0,0 +1,86 @@ +/// @file +/// @brief Task2: definitions of helper functions for the implementation and tests + +#include "task2.misc.hpp" +#include "task2.hpp" // task2::Vec2, task2::BBox, task2::Circle, task2::Triangle + +#include "iue-num/numerics.hpp" // iue::num::isclose + +#include // std::ranges::sort, std::ranges::next_permutation +#include // std::array +#include // std::ostream, std::endl + +namespace task2 { + +std::ostream& operator<<(std::ostream& os, const task2::Vec2d& v) { return os << "( " << v[0] << ", " << v[1] << " )"; } + +std::ostream& operator<<(std::ostream& os, const task2::BBox& v) { + return os << "BBox [ " << std::endl << " min = " << v.min << std::endl << " max = " << v.max << std::endl << "]"; +} + +std::ostream& operator<<(std::ostream& os, const task2::Circle& v) { + return os << "Circle [ " << std::endl + << " center = " << v.c << std::endl + << " radius = " << v.r << std::endl + << "]"; +} + +std::ostream& operator<<(std::ostream& os, const task2::Triangle& v) { + return os << "Triangle [ " << std::endl + << " a = " << v.abc[0] << std::endl + << " b = " << v.abc[1] << std::endl + << " c = " << v.abc[2] << std::endl + << "]"; +} + +bool isclose(const task2::Vec2d& a, const task2::Vec2d& b) { + if (!iue::num::isclose(a[0], b[0])) + return false; + if (!iue::num::isclose(a[1], b[1])) + return false; + return true; +} + +bool isclose(const task2::BBox& a, const task2::BBox& b) { + if (!isclose(a.min, b.min)) + return false; + if (!isclose(a.max, b.max)) + return false; + return true; +} + +bool isclose(const task2::Circle& a, const task2::Circle& b) { + if (!isclose(a.c, b.c)) + return false; + if (!iue::num::isclose(a.r, b.r)) + return false; + return true; +} + +bool isclose(const task2::Triangle& a, const task2::Triangle& b) { + for (int n = 0; n != 3; ++n) + if (!isclose(a.abc[n], b.abc[n])) + return false; + return true; +} + +bool isclose_permute(const task2::Triangle& a, const task2::Triangle& b) { + auto permute = a.abc; + std::ranges::sort(permute); + auto keep = b.abc; + do { + if (isclose({permute}, {keep})) + return true; + } while (std::ranges::next_permutation(permute).found); + return false; +} + +bool isnan(const Vec2d& v) { + if (std::isnan(v[0])) + return true; + if (std::isnan(v[1])) + return true; + return false; +} + +} // namespace task2 diff --git a/exercise4/task2.misc.hpp b/exercise4/task2.misc.hpp new file mode 100644 index 0000000..3341545 --- /dev/null +++ b/exercise4/task2.misc.hpp @@ -0,0 +1,30 @@ +/// @file +/// @brief Task2: declaration of helper functions for the implementation and tests + +#pragma once + +#include "task2.hpp" // task2::Vec2d, task2::BBox, task2::Circle, task2::Triangle + +#include // std::ostream + +namespace task2 { + +/// @brief helper functions to print to the console +std::ostream& operator<<(std::ostream& os, const task2::Vec2d& v); +std::ostream& operator<<(std::ostream& os, const task2::BBox& v); +std::ostream& operator<<(std::ostream& os, const task2::Circle& v); +std::ostream& operator<<(std::ostream& os, const task2::Triangle& v); + +/// @brief helper function checking if a NAN is present +bool isnan(double v); + +/// @brief helper functions for checking if two objects are numerically close +bool isclose(const task2::Vec2d& a, const task2::Vec2d& b); +bool isclose(const task2::BBox& a, const task2::BBox& b); +bool isclose(const task2::Circle& a, const task2::Circle& b); +bool isclose(const task2::Triangle& a, const task2::Triangle& b); + +/// @brief helper function comparing Triangles including permutations of corners +bool isclose_permute(const task2::Triangle& a, const task2::Triangle& b); + +} // namespace task2 diff --git a/exercise4/task2.test.cpp b/exercise4/task2.test.cpp new file mode 100644 index 0000000..132558d --- /dev/null +++ b/exercise4/task2.test.cpp @@ -0,0 +1,133 @@ +/// @file +/// @brief Task2: tests + +#include "task2.hpp" // task2::BBox, task2::Circle, task2::Triangle + +#include "task2.misc.hpp" // task2::operator<<, task2::isclose + +#include // assert +#include // NAN +#include // std::cout, std::endl +#include // std::numbers::pi + +int main() { + + using namespace task2; + + { /// demo: using of functionality provided in 'task2.misc.hpp' + BBox box = {{1, 2}, {3, 4}}; + Circle circle = {{1, 2}, 3}; + Triangle triangle = {Vec2d{0, 0}, Vec2d{1, 0}, Vec2d{1, 1}}; + std::cout << "printing demo:" << std::endl; + std::cout << box << std::endl; + std::cout << circle << std::endl; + std::cout << triangle << std::endl; + std::cout << "isclose demo:" << std::endl; + std::cout << isclose(box, box) << std::endl; + } + + /// ======= BBox ============================= + + { // testing function 'BBox::scale' + BBox box = {Vec2d{-10, 10}, Vec2d{10, 10}}; + box = box.scale({-10, 0}, 2); + BBox expected = {Vec2d{-10, 20}, Vec2d{30, 20}}; + assert(isclose(box, expected)); + } + + { // testing function 'BBox::translate' + BBox box = {Vec2d{-10, 10}, Vec2d{10, 10}}; + box = box.translate({-10, 0}); + BBox expected = {Vec2d{-20, 10}, Vec2d{0, 10}}; + assert(isclose(box, expected)); + } + + { // testing function 'BBox::check_invariants' + BBox box1 = {{NAN, 10}, {20, 20}}; + assert(!box1.check_invariants()); + BBox box2 = {{30, 10}, {20, 20}}; + assert(!box2.check_invariants()); + BBox box3 = {{-100, 10}, {20, 20}}; + assert(box3.check_invariants()); + } + + /// ======= Circle ============================= + + { // testing function 'Circle::bbox' + Circle c = {{10, 10}, 1}; + BBox box = {{9, 9}, {11, 11}}; + assert(isclose(c.bbox(), box)); + } + + { // testing function 'Circle::scale' + Circle c = {{10, 10}, 1}; + c = c.scale({0, 10}, 2); + Circle expected = {{20, 10}, 2}; + assert(isclose(c, expected)); + } + + { // testing function 'Circle::rotate' + Circle c = {{10, 10}, 1}; + c = c.rotate({0, 10}, std::numbers::pi); + Circle expected = {{-10, 10}, 1}; + assert(isclose(c, expected)); + } + + { // testing function 'Circle::translate' + Circle c = {{10, 10}, 1}; + c = c.translate({-20, -20}); + Circle expected = {{-10, -10}, 1}; + assert(isclose(c, expected)); + } + + { // testing function 'Circle::check_invariants' + Circle c1 = {{10, 10}, -2}; + assert(!c1.check_invariants()); + Circle c2 = {{NAN, 10}, 1}; + assert(!c2.check_invariants()); + Circle c3 = {{-10, -10}, 1}; + assert(c3.check_invariants()); + } + + /// ======= Triangle ============================= + + { // testing function 'Triangle::bbox' + Triangle t = {Vec2d{10, 10}, Vec2d{10, 20}, Vec2d{20, 10}}; + BBox box = {{10, 10}, {20, 20}}; + assert(isclose(t.bbox(), box)); + } + + { // testing function 'Triangle::scale' + Triangle t = {Vec2d{10, 10}, Vec2d{10, 20}, Vec2d{20, 10}}; + t = t.scale({10, 20}, 2); + Triangle e = {Vec2d{10, 0}, Vec2d{10, 20}, Vec2d{30, 0}}; + assert(isclose_permute(t, e)); + } + + { // testing function 'Triangle::rotate' + Triangle t = {Vec2d{10, 10}, Vec2d{10, 20}, Vec2d{20, 10}}; + t = t.rotate({10, 20}, -std::numbers::pi / 2); + Triangle e = {Vec2d{10, 20}, Vec2d{0, 20}, Vec2d{0, 10}}; + assert(isclose_permute(t, e)); + } + + { // testing function 'Triangle::translate' + Triangle t = {Vec2d{10, 10}, Vec2d{10, 20}, Vec2d{20, 10}}; + t = t.translate({-10, -10}); + Triangle e = {Vec2d{0, 0}, Vec2d{0, 10}, Vec2d{10, 0}}; + assert(isclose_permute(t, e)); + } + + { // testing function 'Triangle::check_invariants' + Triangle t1 = {Vec2d{NAN, 10}, Vec2d{10, 20}, Vec2d{20, 10}}; + assert(!t1.check_invariants()); + Triangle t2 = {Vec2d{0, 10}, Vec2d{10, 20}, Vec2d{20, 10}}; + assert(t2.check_invariants()); + Triangle t3 = {Vec2d{0, 10}, Vec2d{0, 10}, Vec2d{20, 10}}; + assert(!t3.check_invariants()); + } + + std::cout << "task2.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise4/task3.cpp b/exercise4/task3.cpp new file mode 100644 index 0000000..1d8a9be --- /dev/null +++ b/exercise4/task3.cpp @@ -0,0 +1,55 @@ +/// g++ -g -Imodules -std=c++20 task2.cpp task2.misc.cpp task3.cpp task3.misc.cpp task3.test.cpp -o build/task3.exe +/// ./build/task3.exe +/// @file +/// @brief Task3: implementation + +#include "task3.hpp" // task3::render_something + +#include "task3.misc.hpp" // task3::render_wrapper + +#include "task2.hpp" // task2::BBox, task2::Circle, task2::Triangle + +#include "iue-rnd/random.hpp" // iue::rng::UniformDouble, iue::rng::UniformCircle, iue::rng::UniformTriangle + +#include +#include // std::filesystem::path +#include +#include + +/// @todo Include additional standard library headers as needed + +namespace task3 { + +/// @todo Implement function 'render_something' as declared and specified in task3.hpp +/// Implementation Hints: +/// - You can plot whatever you want, but you need to plot at least 20 primitives! +/// - You can view .svg-files using your web browser (or installing a VSCode extension to preview SVGs) +/// - use the provided function 'task3::render_wrapper' from 'task3.misc.hpp' to plot +/// task2::BBox, task2::Circle, task2::Triangle to a SVG-file +/// - The idea is that you make use of the functionality (scale/shift/rotate) you implemented in task2 +/// - Optional: you can make use of the random Generators in 'iue-rnd/random.hpp' to obtain random values, circles, +/// and triangles +int render_something(std::filesystem::path filepath) { + + task2::Vec2d min = {0, 0}; + task2::Vec2d max = {1, 2}; + task2::BBox trunk = {min, max}; + task2::Circle leaves = {{0.5,0.5},1}; + std::vector bboxes; + std::vector circles; + std::vector triangles; + + + for(int i = 0; i < 20; i++){ + double x_trans=3*i; + bboxes.push_back(trunk.translate({x_trans,0})); + circles.push_back(leaves.translate({x_trans,2})); + } + + auto [bbmin, bbmax] = task3::render_wrapper(filepath, bboxes, circles, triangles); + + return triangles.size() + bboxes.size() + circles.size(); +}; + +} // namespace task3 + diff --git a/exercise4/task3.hpp b/exercise4/task3.hpp new file mode 100644 index 0000000..53db227 --- /dev/null +++ b/exercise4/task3.hpp @@ -0,0 +1,16 @@ +/// @file +/// @brief Task3: function declarations + +#pragma once + +#include // std::filesystem::path + +namespace task3 { + +/// @brief Renders an SVG image +/// @param filepath Filename of the produced SVG-Image +/// @return Total number of geometric primitives (Bbox, Circle, Triangle) plotted +/// @note the content of the image is not restricted, but you need to plot at least 20 primitives +int render_something(std::filesystem::path filepath); + +} // namespace task3 diff --git a/exercise4/task3.misc.cpp b/exercise4/task3.misc.cpp new file mode 100644 index 0000000..9151ce9 --- /dev/null +++ b/exercise4/task3.misc.cpp @@ -0,0 +1,34 @@ +/// @file +/// @brief Task3: definitions of helper functions for the implementation and tests + +#include "task3.misc.hpp" + +#include "task2.hpp" // task2::BBox, task2::Circle, task2::Triangle, + +#include "iue-svg/render.hpp" // iue::svg::render + +#include // std::filesystem::path + +namespace task3 { + +task2::BBox render_wrapper(std::filesystem::path filepath, const std::vector& bboxes, + const std::vector& circles, const std::vector& triangles) { + + auto circle_convert = [](const task2::Circle& in) -> iue::svg::Circle { return {in.c, in.r}; }; + std::vector c; + std::transform(circles.begin(), circles.end(), std::back_inserter(c), circle_convert); + + auto triangle_convert = [](const task2::Triangle& in) -> iue::svg::Triangle { return {in.abc}; }; + std::vector t; + std::transform(triangles.begin(), triangles.end(), std::back_inserter(t), triangle_convert); + + auto bbox_convert = [](const task2::BBox& in) -> iue::svg::BBox { return iue::svg::BBox{in.min, in.max}; }; + std::vector r; + std::transform(bboxes.begin(), bboxes.end(), std::back_inserter(r), bbox_convert); + + auto [bbmin, bbmax] = iue::svg::render(filepath, r, c, t); + + return {bbmin, bbmax}; +} + +} // namespace task3 \ No newline at end of file diff --git a/exercise4/task3.misc.hpp b/exercise4/task3.misc.hpp new file mode 100644 index 0000000..7c1fbae --- /dev/null +++ b/exercise4/task3.misc.hpp @@ -0,0 +1,25 @@ +/// @file +/// @brief Task3: declaration of helper functions for the implementation + +#pragma once + +#include "task2.hpp" // task3::render_something + +#include // std::filesystem::path +#include // std::vector + +namespace task3 { + +/// @brief Wrapper function for 'iue::svg::render' to support direct plotting of the types from task2: +/// task2::BBox, +/// task2::Circle +/// task2::Triangle +/// @param filepath Filename of the produced SVG-Image +/// @param bboxes Sequence of bounding boxes to be plotted +/// @param circles Sequence of circles to be plotted +/// @param triangles Sequence of triangles to be plotted +/// @return Enclosing bounding box of all rendered primitives +task2::BBox render_wrapper(std::filesystem::path filepath, const std::vector& bboxes, + const std::vector& circles, const std::vector& triangles); + +} // namespace task3 diff --git a/exercise4/task3.test.cpp b/exercise4/task3.test.cpp new file mode 100644 index 0000000..87d0230 --- /dev/null +++ b/exercise4/task3.test.cpp @@ -0,0 +1,22 @@ +/// @file +/// @brief Task3: tests + +#include "task3.hpp" // task3::render_something + +#include // assert +#include // std::filesystem::exists +#include // std::cout, sts::endl + +int main() { + + std::string filepath = "task3.test.svg"; + + std::filesystem::remove(filepath); + int count = task3::render_something(filepath); + assert(std::filesystem::exists(filepath)); + assert(count >= 20); + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise5/.clang-format b/exercise5/.clang-format new file mode 100644 index 0000000..0c35b1b --- /dev/null +++ b/exercise5/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise5/.clangd b/exercise5/.clangd new file mode 100644 index 0000000..68a6e4c --- /dev/null +++ b/exercise5/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: +# - --target=x86_64-w64-windows-gnu +# - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/exercise5/.ctests b/exercise5/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise5/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise5/.expected-files b/exercise5/.expected-files new file mode 100644 index 0000000..7ec8475 --- /dev/null +++ b/exercise5/.expected-files @@ -0,0 +1,3 @@ +task1.main.cpp +task2.cpp +task3.cpp diff --git a/exercise5/.gitattributes b/exercise5/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise5/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise5/.gitignore b/exercise5/.gitignore new file mode 100644 index 0000000..efff150 --- /dev/null +++ b/exercise5/.gitignore @@ -0,0 +1,360 @@ +# custom + +*.csv +*.png +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/exercise5/.gitmodules b/exercise5/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/exercise5/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/exercise5/CMakeLists.txt b/exercise5/CMakeLists.txt new file mode 100644 index 0000000..5175c8b --- /dev/null +++ b/exercise5/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise5 LANGUAGES CXX + DESCRIPTION "exercise5" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise5") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.cpp) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "14") + + add_executable(task2 task2.cpp task2.test.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3 TriangleMesh.cpp task2.cpp task3.cpp task3.test.cpp) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + diff --git a/exercise5/README.md b/exercise5/README.md new file mode 100644 index 0000000..79ffbc5 --- /dev/null +++ b/exercise5/README.md @@ -0,0 +1,19 @@ +# Hausübung 5 (3 Punkte) + +**Ausgabe**: Donnerstag 18. April 2024, vormittags. + +**Abgabe bis**: Montag 29. April 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise5`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Arbeiten mit Kollektionen von eindeutig Werten `std::set`/`std::unordered_set` +- Arbeit mit der öffentlichen Schnittstelle einer gegebenen Klasse +- Nutzung von selbst implementierter Funktionalität in einem separaten Anwendungskontext + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise5/TriangleMesh.cpp b/exercise5/TriangleMesh.cpp new file mode 100644 index 0000000..6dfb3dc --- /dev/null +++ b/exercise5/TriangleMesh.cpp @@ -0,0 +1,217 @@ +/// @file +/// @brief TriangleMesh: member function implementation/definitions + +#include "TriangleMesh.hpp" // ex5::TriangleMesh + +#include "iue-svg/render.hpp" // iue::svg::render +#include "iue-svg/tags.hpp" // iue::svg::Triangle + +#include // std::sort, std::ranges::max, std::max +#include // assert +#include // std::filesystem +#include // std::cout, std::endl +#include // std::numeric_limits +#include // std::map +#include // std::set, std::multiset +#include // std::tuple +#include // std::unordered_set +#include // std::pair +#include // std::vector, std::erase_if + +namespace ex5 { + +TriangleMesh::TriangleMesh(TriangleMesh::BBox box, double h) : vertices(), triangles() { + + auto [min, max] = box; + + assert(max[0] > min[0]); + assert(max[1] > min[1]); + + auto width = max[0] - min[0]; + auto height = max[1] - min[1]; + + int nx = (width / h) + 1; + int ny = (height / h) + 1; + + assert(nx >= 2); + assert(ny >= 2); + + vertices.resize(nx * ny); + + for (size_t iy = 0; iy != ny; ++iy) { + for (size_t ix = 0; ix != nx; ++ix) { + vertices.at(ix + iy * nx) = {ix * h + min[0], iy * h + min[1]}; + } + } + + triangles.reserve((nx - 1) * (ny - 1) * 2); + for (size_t iy = 1; iy != ny; ++iy) { + for (size_t ix = 1; ix != nx; ++ix) { + auto i = ix + iy * nx; + auto ia = i - nx - 1; + auto ib = i - nx; + auto ic = i; + auto id = i - 1; + triangles.push_back({ia, ib, id}); + triangles.push_back({ib, ic, id}); + } + } +} + +TriangleMesh::BBox TriangleMesh::bbox() const { + Vec2d bbmin = {+std::numeric_limits::max(), +std::numeric_limits::max()}; + Vec2d bbmax = {-std::numeric_limits::max(), -std::numeric_limits::max()}; + + // update minmax from single coordinate + auto minmax = [&bbmin, &bbmax](const Vec2d& p) { + bbmin[0] = bbmin[0] < p[0] ? bbmin[0] : p[0]; + bbmin[1] = bbmin[1] < p[1] ? bbmin[1] : p[1]; + bbmax[0] = bbmax[0] > p[0] ? bbmax[0] : p[0]; + bbmax[1] = bbmax[1] > p[1] ? bbmax[1] : p[1]; + }; + + for (const auto& v : vertices) { + minmax(v); + } + + return {bbmin, bbmax}; +} + +const std::vector& TriangleMesh::getVertices() const { return vertices; } + +const std::vector& TriangleMesh::getTriangles() const { return triangles; } + +void TriangleMesh::print() const { + std::cout << "Mesh [" << std::endl; + std::cout << " vertices [" << std::endl; + { // print vertices + size_t index = 0; + for (const auto& [x, y] : vertices) { + std::cout << " " << index++ << ": (" << x << ", " << y << " )" << std::endl; + } + std::cout << " ]" << std::endl; + } + { // print triangles + size_t index = 0; + std::cout << " triangles [" << std::endl; + for (const auto& [ia, ib, ic] : triangles) { + std::cout << " " << index++ << ": (" << ia << ", " << ib << ", " << ic << " )" << std::endl; + } + std::cout << " ]" << std::endl; + } + std::cout << "]" << std::endl; +} + +TriangleMesh::BBox TriangleMesh::save(std::filesystem::path filepath) const { + + std::vector tris; + + for (const auto& [ia, ib, ic] : triangles) { + tris.push_back({vertices[ia], vertices[ib], vertices[ic]}); + } + + return iue::svg::render(filepath, {}, {}, tris); +} + +bool TriangleMesh::check_invariants() const { + + std::set vertices_in_use = {}; + std::multiset> edges; + for (auto i3 : triangles) { + std::sort(i3.begin(), i3.end()); + edges.insert({i3[0], i3[1]}); + edges.insert({i3[1], i3[2]}); + edges.insert({i3[2], i3[0]}); + + // check: each triangle references three distinct corners + if (std::set{i3[0], i3[1], i3[2]}.size() != 3) { + return false; + } + + // check: each triangle references valid indices + if (std::ranges::max(i3) >= vertices.size()) { + return false; + } + + vertices_in_use.insert(i3[0]); + vertices_in_use.insert(i3[1]); + vertices_in_use.insert(i3[2]); + } + + // check: edges are not occupied more than twice (no hidden edges) + for (const auto& edge : edges) { + if (edges.count(edge) > 2) { + return false; + } + } + + // check: no unreferenced vertices are present + if (vertices_in_use.size() != vertices.size()) { + return false; + } + + return true; +} + +std::tuple TriangleMesh::remove_vertices(std::unordered_set indices) { + + if (indices.empty()) + return {0, 0}; + + assert(indices.size() != 0); + assert(std::ranges::max(indices) <= vertices.size()); + + // remove connected triangles + auto connected = [&indices](const Vec3i& triangle) -> bool { + const auto& [ia, ib, ic] = triangle; + return indices.contains(ia) || indices.contains(ib) || indices.contains(ic); + }; + auto num_removed_triangles = std::erase_if(triangles, connected); + + // find additional points which became unused (due to removal of triangles) and ... + std::unordered_set in_use; + for (auto& [ia, ib, ic] : triangles) { + in_use.insert(ia); + in_use.insert(ib); + in_use.insert(ic); + } + // .. add those to the list of indices to be removed + for (size_t i = 0; i != vertices.size(); ++i) { + if (in_use.find(i) == in_use.end()) { + indices.insert(i); + } + } + + // remove vertices and record offsets + std::map offsets; + ptrdiff_t offset = 0; + size_t index = 0; + auto remove = [&index, &indices, &offsets, &offset](const Vec2d&) -> bool { + if (indices.contains(index)) { + std::pair pair = {std::max(index - 1, 0), offset--}; + offsets.insert(pair); + ++index; + return true; + } + ++index; + return false; + }; + auto num_removed_vertices = std::erase_if(vertices, remove); + + // add final offset + std::pair pair = {std::max(index - 1, 0), offset}; + offsets.insert(pair); + + // update triangle indices using recorded offsets + for (auto& triangle : triangles) { + for (auto& i : triangle) { + auto iter = offsets.lower_bound(i); + if (iter != offsets.end()) + i += iter->second; + } + } + + return {num_removed_vertices, num_removed_triangles}; +} + +} // namespace ex5 diff --git a/exercise5/TriangleMesh.hpp b/exercise5/TriangleMesh.hpp new file mode 100644 index 0000000..059e507 --- /dev/null +++ b/exercise5/TriangleMesh.hpp @@ -0,0 +1,74 @@ +/// @file +/// @brief TriangleMesh: class definition and member function declarations + +#pragma once + +// hpp +#include // std::array +#include // std::tuple +#include // std::vector +#include // std::unordered_set +#include // std::filesystem + +namespace ex5 { + +///@brief Two-dimensional triangle mesh +class TriangleMesh { + +public: + ///@brief Vertex coordinate + using Vec2d = std::array; + + ///@brief Index triplet + using Vec3i = std::array; + + ///@brief Axis-aligned bounding box described using the bottom left and upper right coordinate + using BBox = std::tuple; + +private: + std::vector vertices; ///< sequence of vertices + std::vector triangles; ///< sequence of index triplets each indexing the three corner vertices of a triangle + +public: + /// @brief Generates a regular triangular mesh pattern inside an axis-aligned bounding box + /// @param box Axis-aligned bounding box of the generated mesh + /// @param h Lower bound for the edge length of the generate triangles + TriangleMesh(BBox box, double h); + + /// @brief Calculates the current axis-aligned bounding box + /// @return Bounding box of all triangles/vertices in the mesh + BBox bbox() const; + + /// @brief Read-only access to the vertices of the mesh + /// @return Reference to the underlying sequence of vertices + const std::vector& getVertices() const; + + /// @brief Read-only access to triangles of the mesh + /// @return Reference to the underlying sequence of triangles + const std::vector& getTriangles() const; + + /// @brief Prints the sequences of vertices and triangles to the console + void print() const; + + /// @brief Generate SVG image + /// @param filepath Filename for the generated output + BBox save(std::filesystem::path filepath) const; + + /// @brief Evaluates the invariants of the class + /// @return false, if any of the invariants is violated, true otherwise + /// Invariants: + /// - each triangle references valid indices + /// - each triangle references three distinct corners + /// - edges are not occupied more than twice (no hidden edges) + /// - no unreferenced vertices are present + bool check_invariants() const; + + /// @brief Removes a set of vertices from the mesh: + /// - all triangles connected to the removed vertices are removed + /// - all unused vertices potentially originating from the triangle removal are removed + /// @param indices Vertex indices to be removed + /// @return Total number of removed vertices and total number of removed triangles + std::tuple remove_vertices(std::unordered_set indices); +}; + +} // namespace mesh diff --git a/exercise5/compile_flags.txt b/exercise5/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise5/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise5/images/annuluses1.svg b/exercise5/images/annuluses1.svg new file mode 100644 index 0000000..f896a1f --- /dev/null +++ b/exercise5/images/annuluses1.svg @@ -0,0 +1,2500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exercise5/images/annuluses2.svg b/exercise5/images/annuluses2.svg new file mode 100644 index 0000000..f853a46 --- /dev/null +++ b/exercise5/images/annuluses2.svg @@ -0,0 +1,2467 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exercise5/images/annuluses3.svg b/exercise5/images/annuluses3.svg new file mode 100644 index 0000000..d862c71 --- /dev/null +++ b/exercise5/images/annuluses3.svg @@ -0,0 +1,3241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/exercise5/main.ipynb b/exercise5/main.ipynb new file mode 100644 index 0000000..e69b1bc --- /dev/null +++ b/exercise5/main.ipynb @@ -0,0 +1,242 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C++-Programm (*std::set*/*std::unordered_set*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::cout, std::endl\n", + "\t#include // std::set, std::unordered_set\n", + "\t#include // std::vector\n", + "\t...\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion in einem eignene Namensraum, z.B.:\n", + "\t```cpp\n", + "\tnamespace task1 {\n", + "\n", + "\t...\n", + "\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet und die berechneten Ergebnisse in der Konsole ausgibt, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\n", + "\t ...\n", + "\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Freie Funktionen zur Selektion von zweidimensionalen Koordinaten (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Die von Ihnen zu implementierenden freien Funktionen wählen aus einer Sequenz von Koordinaten diejenigen aus, die bei Übergabe an eine ebenfalls übergebene Funktion `true` zurückgeben.\n", + "\n", + "Die Rückgabe der ausgewählten Koordinaten erfolgt nicht direkt über eine Liste von Koordinatenwerten, sondern indirekt über eine Liste von Indizes. \n", + "\n", + "Hinweis: In Aufgabe 3 werden Sie Ihre Funktionen im Zusammenhang mit der Klasse `ex5::TriangleMesh` anwenden.\n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie die folgende freie Funktionen:\n", + "\n", + "```cpp\n", + "namespace task2 {\n", + "\n", + "\n", + "using Vec2d = std::array;\n", + "using Circle = std::tuple;\n", + "using BBox = std::tuple;\n", + "using Region = std::function;\n", + "\n", + "// todo\n", + "std::unordered_set select(const std::vector& vertices, \n", + " const Region& region, \n", + " bool invert = false);\n", + "// todo\n", + "std::unordered_set select_union(const std::vector& vertices, \n", + " const std::vector& regions,\n", + " bool invert = false);\n", + "\n", + "} \n", + "\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklarationen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.hpp`](task2.hpp)\n", + "- Ihre Implementierung erfolgt in [`task2.cpp`](task2.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Erzeugen eines Dreiecksgitters für zwei Kreisringe (1 Punkt)\n", + "\n", + "Sie implementieren eine Funktion, die mittels der Klasse `ex5::TriangleMesh` zuerst ein rechteckiges Dreiecksgitter erstellt, und dann mittels\n", + "\n", + "- der von Ihnen implementierten Funktionen `task2::select`/`task2::select_union` und \n", + "- der öffentlichen Schnittstellen der Klasse `ex5::TriangleMesh` \n", + "\n", + "Dreiecke aus dem Gitter entfernt, so dass schlussendlich ein Dreiecksgitter nur innerhalb zweier Kreisringe bestehen bleibt. \n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie folgende Funktion:\n", + "\n", + "```cpp\n", + "\n", + "#include \"TriangleMesh.hpp\" // ex5::TriangleMesh\n", + "\n", + "namespace task3 {\n", + "\n", + "// todo\n", + "ex5::TriangleMesh generate_double_annulus(task2::Vec2d c1, task2::Vec2d c2, double r, double R, double h);\n", + "\n", + "}\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "Dies sieht dann abhängig von den Koordinaten der Kreismittelpunkte, Radien und Gitterauflösung z.B. so aus:\n", + "\n", + "![images/annuluses1.svg](images/annuluses1.svg)\n", + "\n", + "\n", + "\n", + "![images/annuluses3.svg](images/annuluses3.svg)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n", + "- In [`TriangleMesh.hpp`](TriangleMesh.hpp)/[`TriangleMesh.cpp`](TriangleMesh.cpp) finden Sie die vorgegebene Implementierung für `ex5::TriangleMesh`\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `g++` und `python`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "g++ -g -std=c++20 task1.main.cpp -o build/task1\n", + "g++ -g -Imodules -std=c++20 task2.cpp task2.test.cpp -o build/task2\n", + "g++ -g -Imodules -std=c++20 TriangleMesh.cpp task2.cpp task3.cpp task3.test.cpp -o build/task3\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\n", + "./build/task3\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 --verbose\n", + "ctest --test-dir build -C Debug -R task2 --verbose\n", + "ctest --test-dir build -C Debug -R task3 --verbose\n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise5/modules b/exercise5/modules new file mode 160000 index 0000000..d75e4d1 --- /dev/null +++ b/exercise5/modules @@ -0,0 +1 @@ +Subproject commit d75e4d122cc2742724364381cfda7bf381b41a3c diff --git a/exercise5/task1.main.cpp b/exercise5/task1.main.cpp new file mode 100644 index 0000000..6b94fea --- /dev/null +++ b/exercise5/task1.main.cpp @@ -0,0 +1,35 @@ +/// @file +/// @brief Task1: "single-file" excutable C++ program + +/// @todo Include standard library headers as needed + +#include +#include +#include +#include +namespace task1 { + +/// @todo Implement a function 'count_unique' according to the description below: +/// - a sequence of integer values is received via a parameter of type std::vector +/// - the function finds the unique entries in the sequence by inserting each value into a std::unordered_set +/// - the values in a std::unordered_set are unique, so the number of unique entries is the size of the set +/// - the function then returns the number of unique entries +/// @param sequence a sequence of integer values +/// @return the number of unique entries in the sequence +int count_unique(const std::vector& sequence) { + return (std::set (sequence.begin(), sequence.end())).size(); +} + + +} // namespace task1 + +/// - fill a std::vector with with this sequence of values +/// 1,1,10,2,2,3,4,5,6,7,8,9,10,3,3,11,12,13,14 +/// - use your function to count the unique values in the sequence above +/// - print the number of unique values to the console +int main() { + std::vector sequence = {1,1,10,2,2,3,4,5,6,7,8,9,10,3,3,11,12,13,14}; + int unique_Count = task1::count_unique(sequence); + std::cout << "Number of unique values: " << unique_Count << std::endl; + return 0; +} diff --git a/exercise5/task2.cpp b/exercise5/task2.cpp new file mode 100644 index 0000000..e134577 --- /dev/null +++ b/exercise5/task2.cpp @@ -0,0 +1,60 @@ +/// @file +/// @brief Task2: member function definitions/implementations + +#include "task2.hpp" // task2::select, task2::select_union + +#include // std::array +#include // std::function +#include // std::unordered_set +#include // std::vector +#include + + +namespace task2 { + + +/// @brief Selects the subset of vertices for which a boolean function evaluates to true +/// @param vertices Two-dimensional vertices to select from +/// @param region Boolean function to evaluate if a vertex is selected +/// @param invert Inverts the selection process if set to true +/// @return Indices of the selected vertices +std::unordered_set select(const std::vector& vertices, const Region& region, bool invert) { + std::unordered_set selected_indices; + for (size_t i = 0; i < vertices.size(); ++i) { + const bool isSelected = region(vertices[i]); + if ((isSelected && !invert) || (!isSelected && invert)) { + selected_indices.insert(i); + } + } + return selected_indices; +} + + +/// @brief Selects the subset of vertices for which ANY of a sequence of boolean functions evaluates to true +/// @param vertices Two-dimensional vertices to select from +/// @param regions Sequence of boolean functions to evaluate +/// @param invert Inverts the selection process if set to 'true' +/// @return Indices of the selected vertices +std::unordered_set select_union(const std::vector& vertices, const std::vector& regions, bool invert) { + std::unordered_set selected_indices; + for (size_t i = 0; i < vertices.size(); ++i) { + bool isSelected = false; + // Check if any region function evaluates to true + for (const Region& region : regions) { + isSelected = isSelected || region(vertices[i]); + if (isSelected && !invert) { + // Exit inner loop if selected and not inverting + break; + } + } + if (isSelected && invert) { + // Add to selected set only if inverting and not already selected + } else { + // Add to selected set for normal selection or inverted with not yet selected + selected_indices.insert(i); + } + } + return selected_indices; +} + +} // namespace task2 \ No newline at end of file diff --git a/exercise5/task2.hpp b/exercise5/task2.hpp new file mode 100644 index 0000000..0922e85 --- /dev/null +++ b/exercise5/task2.hpp @@ -0,0 +1,42 @@ +/// @file +/// @brief Task2: class definitions with member function declarations + +#pragma once + +#include // std::array +#include // std::function +#include // std::unordered_set +#include // std::vector + +namespace task2 { + +/// @brief two-dimensional coordinate +using Vec2d = std::array; + +/// @brief Circle with center and radius +using Circle = std::tuple; + +/// @brief Axis-aligned bounding box with bottom left and top right coordinate) +using BBox = std::tuple; + +/// @brief Arbitrary two-dimensional region described by a boolean function returning +/// - true, if the point/coordinate in question lies inside this region, and +/// - false, otherwise. +using Region = std::function; + +/// @brief Selects the subset of vertices for which a boolean function evaluates to true +/// @param vertices Two-dimensional vertices to select from +/// @param region Boolean function to evaluate if a vertex is selected +/// @param invert Inverts the selection process if set to true +/// @return Indices of the selected vertices +std::unordered_set select(const std::vector& vertices, const Region& region, bool invert = false); + +/// @brief Selects the subset of vertices for which ANY of a sequence of boolean functions evaluates to true +/// @param vertices Two-dimensional vertices to select from +/// @param regions Sequence of boolean functions to evaluate +/// @param invert Inverts the selection process if set to 'true' +/// @return Indices of the selected vertices +std::unordered_set select_union(const std::vector& vertices, const std::vector& regions, + bool invert = false); + +} // namespace task2 diff --git a/exercise5/task2.test.cpp b/exercise5/task2.test.cpp new file mode 100644 index 0000000..628d300 --- /dev/null +++ b/exercise5/task2.test.cpp @@ -0,0 +1,86 @@ +/// @file +/// @brief Task2: tests + +#include "task2.hpp" // task2::select, task2::select_union + +#include "iue-rnd/random.hpp" // iue::rnd::UniformValue + +#include // std::sort +#include // std::array +#include // assert +#include // std::sqrt +#include // std::cout, std::endl +#include // std::numbers::pi + +int main() { + + using namespace task2; + + std::vector vertices; + + // upper diagonal halfspace region + auto upper_diagonal_halfspace = [](const Vec2d& coord) -> bool { + auto [x, y] = coord; + if (y > x && std::abs(x - y) > 1e-9) + return true; + return false; + }; + + { // adding random vertices in upper diagonal halfspace + auto gen = iue::rnd::UniformValue(-1.0, 1.0); + for (int n = 0; n != 1e2; ++n) { + std::array xy{gen(), gen()}; + std::ranges::sort(xy); + vertices.push_back({xy}); + } + } + + // testing task::select + + { + auto subset = task2::select(vertices, upper_diagonal_halfspace, false); + assert(subset.size() == vertices.size()); + } + + { + auto subset = task2::select(vertices, upper_diagonal_halfspace, true); + assert(subset.size() == 0); + } + + // testing task::select_union + + // circular region around center(1,1) with radius 1 + Circle circle = {{1, 1}, 1}; + auto circle_c11_r1 = [&circle](const Vec2d& coord) -> bool { + auto [c, r] = circle; + auto [cx, cy] = c; + auto [x, y] = coord; + return std::sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) < r ? true : false; + }; + + { // adding random vertices inside the circle + auto [center, r] = circle; + auto gen_angle = iue::rnd::UniformValue(0, std::numbers::pi * 2); + auto gen_radius = iue::rnd::UniformValue(0, r - 1e-9); + for (int n = 0; n != 1e3; ++n) { + double angle = gen_angle(); + double radius = gen_radius(); + std::array xy{center[0] + std::sin(angle) * radius, center[1] + std::cos(angle) * radius}; + vertices.push_back({xy}); + } + } + + { + auto subset = task2::select_union(vertices, {upper_diagonal_halfspace, circle_c11_r1}, false); + assert(subset.size() == vertices.size()); + } + + { + auto subset = task2::select_union(vertices, {upper_diagonal_halfspace, circle_c11_r1}, true); + assert(subset.size() == 0); + } + + std::cout << "task2.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise5/task3.cpp b/exercise5/task3.cpp new file mode 100644 index 0000000..a413b15 --- /dev/null +++ b/exercise5/task3.cpp @@ -0,0 +1,78 @@ +/// @file +/// @brief Task3: implementation + +#include "task3.hpp" // task3::generate_double_annulus +#include "task2.hpp" // task2::select, task2::select_union + +#include "TriangleMesh.hpp" // ex5::TriangleMesh + +#include // for std::sqrt + +namespace task3 { + +using namespace task2; + + +/// @todo Implement function 'generate_double_annulus' as declared and specified in task3.hpp +/// Implementation Hints: +/// - calculate the required bounding box for the annuluses using the centers coordinates +- outer radius +/// - create a triangle mesh using the desired bounding box and resolution (h) +/// - use the source code of task2.test.cpp for ideas how to create the boolean functions for circular regions +/// - select the union of the two inner circles and remove the triangles using the following functions: +/// mesh.getVertices +/// task2::select_union(...) +/// mesh.remove_vertices(...) +/// - analoguously, select the union of the two outer circles and remove the INVERSE of this union using +/// - mesh.getVertices() +/// - task2::select_union(..., true) +/// - mesh.remove_vertices(...) + + +/// @brief Generates a triangle mesh placed of two (potentially merged) annuluses (Kreisringe) +/// @param c1 Center coordinate of the first annulus +/// @param c2 Center coordinate of the second annulus +/// @param r inner radius of both annuluses +/// @param R outer radius of both annuluses +/// @param h lower bound for the triangle egde length of the resulting mesh +/// @return Resulting triangle mesh +ex5::TriangleMesh generate_double_annulus(task2::Vec2d c1, task2::Vec2d c2, double r, double R, double h) { + // Calculate bounding box based on center coordinates, radii and a small offset + double offset = h / 2.0; // Adjust as needed + + auto bb_min = task2::Vec2d{std::min(c1[0], c2[0]) - R - offset, + std::min(c1[1], c2[1]) - R - offset}; + auto bb_max = task2::Vec2d{std::max(c1[0], c2[0]) + R + offset, + std::max(c1[1], c2[1]) + R + offset}; + + // Create triangle mesh using bounding box and edge length + ex5::TriangleMesh mesh(ex5::TriangleMesh::BBox{bb_min, bb_max}, h); + + // Function to select points within circles (assuming select_union is defined in task2) + auto in_circle = [&](const task2::Vec2d& p, task2::Vec2d center, double radius) { + return std::sqrt(std::pow(p[0] - center[0], 2) + std::pow(p[1] - center[1], 2)) <= radius; + }; + + // Select and remove vertices inside both inner circles (union) + std::unordered_set remove_inner; + for (size_t i = 0; i < mesh.getVertices().size(); ++i) { + const auto& v = mesh.getVertices()[i]; + if (in_circle(v, c1, r) && in_circle(v, c2, r)) { + remove_inner.insert(i); + } + } + mesh.remove_vertices(remove_inner); + + // Select and remove vertices outside both outer circles (union - inverted) + std::unordered_set remove_outer; + for (size_t i = 0; i < mesh.getVertices().size(); ++i) { + const auto& v = mesh.getVertices()[i]; + if (!in_circle(v, c1, R) && !in_circle(v, c2, R)) { + remove_outer.insert(i); + } + } + mesh.remove_vertices(remove_outer); + + return mesh; +} + +} // namespace task3 diff --git a/exercise5/task3.hpp b/exercise5/task3.hpp new file mode 100644 index 0000000..4bb4dae --- /dev/null +++ b/exercise5/task3.hpp @@ -0,0 +1,21 @@ +/// @file +/// @brief Task3: function declarations + +#pragma once + +#include "task2.hpp" // task2::Vec2d + +#include "TriangleMesh.hpp" // ex5::TriangleMesh + +namespace task3 { + +/// @brief Generates a triangle mesh placed of two (potentially merged) annuluses (Kreisringe) +/// @param c1 Center coordinate of the first annulus +/// @param c2 Center coordinate of the second annulus +/// @param r inner radius of both annuluses +/// @param R outer radius of both annuluses +/// @param h lower bound for the triangle egde length of the resulting mesh +/// @return Resulting triangle mesh +ex5::TriangleMesh generate_double_annulus(task2::Vec2d c1, task2::Vec2d c2, double r, double R, double h); + +} // namespace task3 diff --git a/exercise5/task3.test.cpp b/exercise5/task3.test.cpp new file mode 100644 index 0000000..6975cf8 --- /dev/null +++ b/exercise5/task3.test.cpp @@ -0,0 +1,37 @@ +/// @file +/// @brief Task3: tests + +#include "task3.hpp" // task3::generate_double_annulus +#include "TriangleMesh.hpp" // ex5::TriangleMesh + +#include // assert +#include // std::filesystem::exists +#include // std::cout, sts::endl +#include // std::string + +int main() { + + using namespace task2; + + std::string filepath = "task3.test.svg"; + + task2::Vec2d c1 = {5, 5}; + task2::Vec2d c2 = {10, 10}; + double r = 3; + double R = 4; + double h = 0.15; + auto mesh = task3::generate_double_annulus(c1, c2, r, R, h); + std::filesystem::remove(filepath); + mesh.save(filepath); + assert(std::filesystem::exists(filepath)); + auto bbox = mesh.bbox(); + auto [bbmin, bbmax] = bbox; + assert(bbmin[0] <= c1[0] - R + 2 * h); + assert(bbmax[0] >= c2[0] + R - 2 * h); + assert(bbmin[1] <= c1[1] - R + 2 * h); + assert(bbmax[1] >= c2[1] + R - 2 * h); + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise6/.clang-format b/exercise6/.clang-format new file mode 100644 index 0000000..3cbc427 --- /dev/null +++ b/exercise6/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 140 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise6/.clangd b/exercise6/.clangd new file mode 100644 index 0000000..68a6e4c --- /dev/null +++ b/exercise6/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: +# - --target=x86_64-w64-windows-gnu +# - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/exercise6/.ctests b/exercise6/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise6/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise6/.expected-files b/exercise6/.expected-files new file mode 100644 index 0000000..e985316 --- /dev/null +++ b/exercise6/.expected-files @@ -0,0 +1,4 @@ +task1.main.cpp +task2.Circle.hpp +task2.Polyline.hpp +task3.cpp diff --git a/exercise6/.gitattributes b/exercise6/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise6/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise6/.gitignore b/exercise6/.gitignore new file mode 100644 index 0000000..efff150 --- /dev/null +++ b/exercise6/.gitignore @@ -0,0 +1,360 @@ +# custom + +*.csv +*.png +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/exercise6/CMakeLists.txt b/exercise6/CMakeLists.txt new file mode 100644 index 0000000..e8f3e45 --- /dev/null +++ b/exercise6/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise6 LANGUAGES CXX + DESCRIPTION "exercise6" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise6") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.cpp) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "malloc.*3.*free") + + add_executable(task2 task2.test.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3 task3.cpp task3.test.cpp) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + diff --git a/exercise6/README.md b/exercise6/README.md new file mode 100644 index 0000000..cc5f623 --- /dev/null +++ b/exercise6/README.md @@ -0,0 +1,18 @@ +# Hausübung 6 (3 Punkte) + +**Ausgabe**: Donnerstag 25. April 2024, vormittags. + +**Abgabe bis**: Montag 6. Mai 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise6`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Arbeiten mit Schnittstellenklassen (hier: *abstract base class*) +- Dynamische Speicherallokation (`new`) und Freigabe (`delete`) + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise6/compile_flags.txt b/exercise6/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise6/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise6/images/task3_output.svg b/exercise6/images/task3_output.svg new file mode 100644 index 0000000..1d32550 --- /dev/null +++ b/exercise6/images/task3_output.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/exercise6/main.ipynb b/exercise6/main.ipynb new file mode 100644 index 0000000..a0ff9b3 --- /dev/null +++ b/exercise6/main.ipynb @@ -0,0 +1,228 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C++-Programm (*new*/*delete*/*abstract base class*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::cout, std::endl\n", + "\t#include // std::vector\n", + "\t...\n", + "\t```\n", + "- Einbinden einer bestehenden Klassenhierarchie \n", + "\t```cpp\n", + "\t#include \"task1.hierarchy.hpp\"\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die mit dynamische allozierte Instanzen der Klassen aus der Klassenhierarchie arbeitet , z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\n", + "\t auto ptr = new Circle(5);\t\t\n", + "\t ...\n", + "\n", + "\t std::cout << ptr->area() << std::endl;\n", + "\n", + "\t delete ptr;\n", + "\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Erweitern einer gegebenen Klassenhierarchie (Implementierung weiterer abgeleiteter Klassen) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Es ist eine abstrakten Schnittstellenklasse für die Serialisierung eines XML-Elements `task2::Element` gegeben.\n", + "Ebenso ist als Beispiel die Implementierung `task2::LineElement` gegeben, die diese Schnittstelle implementiert.\n", + "\n", + "Sie sollen zwei weitere Implementierung der Schnittstelle `task2::PolylineElement` und `task2::CircleElement` analog zu `task2::LineElement` implementieren.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementieren Sie die folgende freie Funktionen:\n", + "\n", + "```cpp\n", + "namespace task2 {\n", + "\n", + "struct Element {\n", + " virtual std::string open() const = 0; ///< returns the opening or self-closing tag of an XML element\n", + " virtual std::string text() const = 0; ///< returns the content tag of an XML element\n", + " virtual std::string close() const = 0; ///< returns the closing tag of an XML element\n", + "};\n", + "\n", + "struct LineElement : Element; // provided\n", + "struct PolylineElement : Element; // todo\n", + "struct CircleElement : Element; // todo\n", + "\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung der Schnittstellenklasse finden Sie in [`task2.Element.hpp`](task2.Element.hpp)\n", + "- Das vorgegebene Beispiel finden Sie in [`task2.Line.hpp`](task2.Line.hpp)\n", + "- Ihre Implementierung erfolgt in [`task2.Polyline.hpp`](task2.Polyline.hpp) und [`task2.Circle.hpp`](task2.Circle.hpp)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Arbeiten mit einer gegebenen Klasse, die dynamisch allozierte Resourcen verwaltet, und an die dynamisch allozierte Resourcen übergeben werden (1 Punkt)\n", + "\n", + "Sie implementieren eine Funktion, die mittels ein SVG-Bild erzeugt und abspeichert:\n", + "\n", + "```cpp\n", + "task2::BBox plot_image(std::filesystem::path filepath, \n", + " const std::vector& lines, \n", + " const std::vector& polylines,\n", + " const std::vector& circles);\n", + "```\n", + "\n", + "Zur Implementierung dieser kommt die Klasse `task3::Image` zum Einsatz. Die Schnittstellen dieser Klasse basieren auf `task2::Element`.\n", + "\n", + "```cpp\n", + "namespace task3 {\n", + "\n", + "class Image {\n", + "public:\n", + " Image(Element* root) : root(root), elements() {}\n", + " void add(Element* elem) { elements.push_back(elem); }\n", + " void save(std::filesystem::path filepath) const;\n", + " ~Image();\n", + "private:\n", + " Element* root; \n", + " std::vector elements;\n", + "};\n", + "\n", + "}\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt in [`task3.cpp`](task3.cpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n", + "- In [`task3.Image.hpp`](task3.Image.hpp) finden Sie die vorgegebene Implementierung für `task3::Image`\n", + "\n", + "- Optional (für Interessierte): In [`task3.ImageUP.hpp`](task3.ImageUP.hpp) finden Sie eine alternative Implementierung `task3::ImageUP` die mit `std::unique_ptr` arbeitet.\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unter Voraussetzung einer erfolgreicher Implementierung von Aufgabe 2 erzeugt der Test in Aufgabe 3 folgende Ausgabe:\n", + "\n", + "![images/task3_output.svg](images/task3_output.svg)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `g++` und `python`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "g++ -g -std=c++20 task1.main.cpp -o build/task1\n", + "g++ -g -Imodules -std=c++20 task2.test.cpp -o build/task2\n", + "g++ -g -Imodules -std=c++20 task3.cpp task3.test.cpp -o build/task3\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\n", + "./build/task3\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):s\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 \n", + "ctest --test-dir build -C Debug -R task2 \n", + "ctest --test-dir build -C Debug -R task3 \n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise6/task1.hierarchy.hpp b/exercise6/task1.hierarchy.hpp new file mode 100644 index 0000000..041950b --- /dev/null +++ b/exercise6/task1.hierarchy.hpp @@ -0,0 +1,27 @@ +/// @file +/// @brief Task1: simple hierarchy to be used in task1.main.cpp + +#pragma once + +#include // std::numbers::pi + +namespace task1 { + +struct Interface { + double virtual area() const = 0; + virtual ~Interface() {} +}; + +struct Circle : Interface { + Circle(double radius) : radius(radius) {} + double radius; + double virtual area() const final override { return std::numbers::pi * radius * radius; } +}; + +struct Square : Interface { + Square(double length) : length(length) {} + double length; + double virtual area() const final override { return length * length; } +}; + +} // namespace task1 \ No newline at end of file diff --git a/exercise6/task1.main.cpp b/exercise6/task1.main.cpp new file mode 100644 index 0000000..eccaa80 --- /dev/null +++ b/exercise6/task1.main.cpp @@ -0,0 +1,57 @@ +/// @file +/// @brief Task1: "single-file" excutable C++ program + +#include "task1.hierarchy.hpp" // task1::Interface, task1::Circle, task1::Rectangle +#include +#include // for std::vector + +/// @todo Include standard library headers as needed + +/// @todo Implement a main funtion conducting the following tasks in this order: +/// - create an empty vector of base pointers 'std::vector' +/// - dynamically allocate 5 Circles with radii set to 1,2,3,4, and 5 +/// - push the pointers to the dynamically allocated circles into the vector +/// - dynamically allocate 5 Squares with length set to 1,2,3,4, and 5 +/// - push the pointers to the dynamically allocated squares into the vector +/// - use a loop to iterate over the vector and count how many elements have an area less than 8.0 +/// - print the result to the console +/// - use a loop to deallocate all 10 elements in the vector + +/// @file +/// @brief Task1: "single-file" excutable C++ program + +/// @todo Implement a main funtion conducting the following tasks in this order: +int main() { + // Create an empty vector of base pointers + std::vector shapes; + + // Dynamically allocate 5 Circles + for (int i = 1; i <= 5; ++i) { + shapes.push_back(new task1::Circle(i)); + std::cout << "malloc - Circle with radius " << i << std::endl; + } + + // Dynamically allocate 5 Squares + for (int i = 1; i <= 5; ++i) { + shapes.push_back(new task1::Square(i)); + std::cout << "malloc - Square with length " << i << std::endl; + } + + // Count elements with area less than 8.0 + int count = 0; + for (auto* shape : shapes) { + if (shape->area() < 8.0) { + count++; + } + } + std::cout << "Number of shapes with area less than 8.0: " << count << std::endl; + + // Deallocate all elements in the vector + for (auto* shape : shapes) { + delete shape; + std::cout << "free - Shape" << std::endl; + } + + return 0; +} + diff --git a/exercise6/task2.Circle.hpp b/exercise6/task2.Circle.hpp new file mode 100644 index 0000000..dc66193 --- /dev/null +++ b/exercise6/task2.Circle.hpp @@ -0,0 +1,43 @@ +/// @file +/// @brief Task2: SVG-Circle element https://developer.mozilla.org/en-US/docs/Web/SVG/Element/circle + +#pragma once + +#include "task2.Element.hpp" +#include "task2.aliases.hpp" + +/// @todo Include standard library headers as needed +#include +#include + +namespace task2 { + +/// @brief Circle SVG Element +struct CircleElement : Element { + Circle circle; // Now uses the alias Circle = std::tuple + + CircleElement(Circle circle) : circle(circle) {} + + std::string open() const override final { + std::stringstream ss; + ss << ""; + return ss.str(); + } + + std::string text() const override final { + return std::string(); + } + + std::string close() const override final { + return std::string(); + } +}; + +} // namespace task2 diff --git a/exercise6/task2.Element.hpp b/exercise6/task2.Element.hpp new file mode 100644 index 0000000..35fb72f --- /dev/null +++ b/exercise6/task2.Element.hpp @@ -0,0 +1,25 @@ +/// @file +/// @brief Task2: Abstract base class used as interface to implement serialization of XML elements + +#pragma once + +#include // std::string + +namespace task2 { + +///@brief XML element interface +struct Element { + + virtual std::string open() const = 0; ///< returns the opening or self-closing tag of an XML element + virtual std::string text() const = 0; ///< returns the content tag of an XML element + virtual std::string close() const = 0; ///< returns the closing tag of an XML element + + virtual ~Element() = default; ///< enable deletion of derived object instances via a base pointer + +protected: ///< only accessible form within a derived class + Element() = default; ///< default constructor + Element(const Element&) = default; ///< copy constructor + Element& operator=(const Element&) = default; ///< copy assignment +}; + +} // namespace task2 diff --git a/exercise6/task2.Line.hpp b/exercise6/task2.Line.hpp new file mode 100644 index 0000000..4697fd7 --- /dev/null +++ b/exercise6/task2.Line.hpp @@ -0,0 +1,42 @@ +/// @file +/// @brief Task2: SVG-Line element https://developer.mozilla.org/en-US/docs/Web/SVG/Element/line + + +#pragma once + +#include "task2.Element.hpp" +#include "task2.aliases.hpp" + +#include +#include + +namespace task2 { + +/// @brief Line SVG Element +/// open: +/// text: empty string +/// close: empty string +struct LineElement : Element { + LineElement(Line line) : line(line) { } + std::string open() const override final { + auto [a, b] = line; + std::stringstream ss; + ss << ""; + return ss.str(); + } + std::string close() const override final { return std::string(); } + std::string text() const override final { return std::string(); } + +private: + Line line; +}; + +} // namespace task2 diff --git a/exercise6/task2.Polyline.hpp b/exercise6/task2.Polyline.hpp new file mode 100644 index 0000000..3a24a31 --- /dev/null +++ b/exercise6/task2.Polyline.hpp @@ -0,0 +1,48 @@ +/// @file +/// @brief Task2: SVG-Polyline element https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polyline + +#pragma once + +#include "task2.Element.hpp" +#include "task2.aliases.hpp" + +/// @todo Include standard library headers as needed +#include + +namespace task2 { + +/// @brief Polyline SVG Element +/// @todo Implement the constructor from a task2::Polyline +/// @todo Implement the class to return the fowllowing strings on the interface functions: +/// open: +/// text: empty string +/// close: empty string +/// @note You can use/add member variables to the class as you see fit +struct PolylineElement : Element { + Polyline polyline; + + PolylineElement(Polyline polyline) { + this->polyline = polyline; + } + std::string open() const override final { + std::stringstream ss; + ss << ""; + return ss.str(); + } + std::string close() const override final { + return std::string(); + } + std::string text() const override final { + return std::string(); + } +}; + +} // namespace task2 diff --git a/exercise6/task2.aliases.hpp b/exercise6/task2.aliases.hpp new file mode 100644 index 0000000..e34f44b --- /dev/null +++ b/exercise6/task2.aliases.hpp @@ -0,0 +1,18 @@ +/// @file +/// @brief Task2: Convenience aliases for short notations for two-dimensional integer coordinates + +#pragma once + +#include // std::array +#include // std::tuple +#include // std::vector + +namespace task2 { + +using Vec2i = std::array; ///< two-dimensional integer coordinate +using BBox = std::tuple; ///< bounding box (bottom-left and top-right corner) +using Circle = std::tuple; ///< circle (center and radius) +using Line = std::tuple; ///< line (start and end point) +using Polyline = std::vector; ///< polyline (sequence of points) + +} // namespace task2 diff --git a/exercise6/task2.test.cpp b/exercise6/task2.test.cpp new file mode 100644 index 0000000..85b3449 --- /dev/null +++ b/exercise6/task2.test.cpp @@ -0,0 +1,69 @@ +/// @file +/// @brief Task2: tests + +#include "task2.Circle.hpp" +#include "task2.Line.hpp" +#include "task2.Polyline.hpp" +#include "task2.aliases.hpp" + +#include // assert +#include // std::cout, sts::endl +#include // std::string + +bool contains(const std::string& str, const std::string& pattern) { return str.find(pattern) != std::string::npos; } + +bool check_defaults(const std::string& str) { + if (!contains(str, "stroke-width='1'")) + return false; + if (!contains(str, "fill='none'")) + return false; + if (!contains(str, "stroke='black'")) + return false; + return true; +} + +int main() { + + { // LineElement + auto line = task2::Line{{0, 0}, {11, 12}}; + auto elem = task2::LineElement(line); + auto openstr = elem.open(); + assert(check_defaults(openstr)); + assert(openstr.starts_with("")); + assert(elem.text().empty() && elem.close().empty()); + } + + { // CircleElement + auto circle = task2::Circle{{5, 6}, 5}; + auto elem = task2::CircleElement(circle); + auto openstr = elem.open(); + assert(check_defaults(openstr)); + assert(openstr.starts_with("")); + assert(elem.text().empty() && elem.close().empty()); + } + + { // PolylineElement + auto polyline = task2::Polyline{{1, 2}, {3, 4}, {5, 6}}; + auto elem = task2::PolylineElement(polyline); + auto openstr = elem.open(); + assert(check_defaults(openstr)); + assert(openstr.starts_with("")); + assert(elem.text().empty() && elem.close().empty()); + } + + std::cout << "task2.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise6/task3.Image.hpp b/exercise6/task3.Image.hpp new file mode 100644 index 0000000..793010f --- /dev/null +++ b/exercise6/task3.Image.hpp @@ -0,0 +1,63 @@ +/// @file +/// @brief Task3: Simple SVG-Writer + +#pragma once + +#include "task2.Element.hpp" // task2::Element + +#include // std::filesystem::path +#include // std::ofstream +#include // std::endl +#include // std::vector + +namespace task2 { + +/// @brief Simple SVG-Writer using the task2::Element interface +class Image { + +public: + /// @brief Constructs an Image from a root SVG element + /// @param root Pointer to a dynamically allocated element + /// - ownership of the dynamic resource (pointer) is transfered to the class instance + Image(Element* root) : root(root), elements() {} + /// @brief Adds a SVG element inside the root element + /// @param elem Pointer to a dynamically allocated element + /// - ownership of the dynamic resource (pointer) is transfered to the class instance + void add(Element* elem) { elements.push_back(elem); } + /// @brief Writes the Image to a file + /// @param filepath Filename of the image to be written + void save(std::filesystem::path filepath) const { + + std::ofstream fs; + fs.exceptions(std::ifstream::badbit); + fs.open(filepath); + + fs << root->open() << std::endl; + if (!root->text().empty()) + fs << root->text() << std::endl; + + for (const auto& e : elements) { + fs << " " << e->open() << std::endl; + if (!e->text().empty()) + fs << " " << e->text() << std::endl; + if (!e->close().empty()) + fs << " " << e->close() << std::endl; + } + + if (!root->close().empty()) + fs << root->close() << std::endl; + } + /// @brief Destructor + /// @note Deallocates all dynamic resources owned by this instance + ~Image() { + for (auto& e : elements) + delete e; + delete root; + } + +private: + Element* root; ///< root element containing all other elements + std::vector elements; ///< sequence of elements +}; + +} // namespace task2 diff --git a/exercise6/task3.ImageUP.hpp b/exercise6/task3.ImageUP.hpp new file mode 100644 index 0000000..0ed8338 --- /dev/null +++ b/exercise6/task3.ImageUP.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "task2.Element.hpp" // task2::Element + +#include // std::filesystem::path +#include // std::ofstream +#include // std::unique_ptr +#include // std::endl +#include // std::move +#include // std::vector + +namespace task2 { + +/// @brief Simple SVG-Writer using the task2::Element interface +class Image { + +public: + /// @brief Constructs an Image from a root SVG element + /// @param root Pointer to the root element + Image(std::unique_ptr root) : root(std::move(root)), elements() {} + /// @brief Adds a SVG element inside the root element + /// @param elem Pointer to the element + void add(std::unique_ptr elem) { elements.emplace_back(std::move(elem)); } + /// @brief Writes the Image to a file + /// @param filepath Filename of the image to be written + void save(std::filesystem::path filepath) const { + + std::ofstream fs; + fs.exceptions(std::ifstream::badbit); + fs.open(filepath); + + fs << root->open() << std::endl; + if (!root->text().empty()) + fs << root->text() << std::endl; + + for (const auto& e : elements) { + fs << " " << e->open() << std::endl; + if (!e->text().empty()) + fs << " " << e->text() << std::endl; + if (!e->close().empty()) + fs << " " << e->close() << std::endl; + } + + if (!root->close().empty()) + fs << root->close() << std::endl; + } + +private: + std::unique_ptr root; ///< root element containing all other elements + std::vector> elements; ///< sequence of elements +}; + +} // namespace task2 diff --git a/exercise6/task3.Root.hpp b/exercise6/task3.Root.hpp new file mode 100644 index 0000000..e39ccd8 --- /dev/null +++ b/exercise6/task3.Root.hpp @@ -0,0 +1,55 @@ +/// @file +/// @brief Task2: SVG-Root element https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg + +#pragma once + +#include "task2.Element.hpp" // task2::Element +#include "task2.aliases.hpp" // task2::BBox + +#include // std::stringstream +#include // std::endl +#include // std::string + +namespace task2 { + +/// @brief Root SVG Element (combined a and a element) +/// @note The group element is used to enable a common transform function for all contained elements to enable a bottom-left origin +/// (the svg-default is top-left) +/// open: +/// +/// text: empty string +/// close: +/// +struct RootElement : Element { + RootElement(BBox bbox) : bbox(bbox) {} + std::string open() const override final { + auto [min, max] = bbox; + double xorg = min[0]; + double yorg = min[1]; + double w = max[0] - min[0]; + double h = max[1] - min[1]; + std::stringstream ss; + ss << "" << std::endl; + ss << ""; + return ss.str(); + } + std::string close() const override final { + std::stringstream ss; + ss << "" << std::endl; + ss << ""; + return ss.str(); + }; + std::string text() const override final { return std::string(); } + +private: + BBox bbox; +}; + +} // namespace task2 diff --git a/exercise6/task3.cpp b/exercise6/task3.cpp new file mode 100644 index 0000000..37b20a1 --- /dev/null +++ b/exercise6/task3.cpp @@ -0,0 +1,33 @@ + +#include "task3.hpp" +#include "task3.Image.hpp" +#include "task3.Root.hpp" +#include "task2.Line.hpp" +#include "task2.Polyline.hpp" +#include "task2.Circle.hpp" + +namespace task3 { + + + +using namespace task2; + +BBox plot_image(std::filesystem::path filepath, const std::vector& lines, const std::vector& polylines, + const std::vector& circles) { + + // Initialize bounding box with a default value + Vec2i min_point = {INT_MAX, INT_MAX}; + Vec2i max_point = {INT_MIN, INT_MIN}; + + // Iterate over all lines + for (const auto& line : lines) { + // Update bounding box + auto [p1, p2] = line; + min_point = min(min_point, line.bbox().min); + max_point = max(max_point, line.bbox().max); + } + + return BBox{min_point, max_point}; +} + +} // namespace task3 \ No newline at end of file diff --git a/exercise6/task3.hpp b/exercise6/task3.hpp new file mode 100644 index 0000000..135eddd --- /dev/null +++ b/exercise6/task3.hpp @@ -0,0 +1,22 @@ +/// @file +/// @brief Task3: declaration + +#pragma once + +#include "task2.aliases.hpp" // task2::Line, task2::Polyline, task2::Circle + +#include // std::filesystem::path +#include // std::vector + +namespace task3 { + +/// @brief Plots an SVG Image containing lines, polylines and circles +/// @param filepath Filename of the image to be written +/// @param lines Sequence of lines to be plotted +/// @param polylines Sequence of polylines to be plotted +/// @param circles Sequence of circles to be plotted +/// @return Smallest bounding box fully containing all plotted shapes (for circles the area is also considered as extend of the shape) +task2::BBox plot_image(std::filesystem::path filepath, const std::vector& lines, const std::vector& polylines, + const std::vector& circles); + +} // namespace task3 diff --git a/exercise6/task3.test.cpp b/exercise6/task3.test.cpp new file mode 100644 index 0000000..54be0d0 --- /dev/null +++ b/exercise6/task3.test.cpp @@ -0,0 +1,45 @@ +/// @file +/// @brief Task3: tests + +#include "task3.hpp" +#include "task2.aliases.hpp" + +#include // assert +#include // std::filesystem::exists +#include // std::cout, sts::endl +#include // std::string +#include // std::this_thread::sleep_for, std::chrono_literals + +int main() { + + std::string filepath = "task3.test.svg"; + + auto hori1 = task2::Line{{-50, 0}, {50, 0}}; + auto vert1 = task2::Line{{0, -50}, {0, 50}}; + auto hori2 = task2::Line{{-50 + 110, 0}, {50 + 110, 0}}; + auto vert2 = task2::Line{{0 + 110, -50}, {0 + 110, 50}}; + std::vector lines = {hori1, vert1, hori2, vert2}; + + auto cline = task2::Polyline{{-60, 50}, {-90, 30}, {-110, 0}, {-90, -30}, {-60, -50}}; + std::vector polylines = {cline}; + + auto circle = task2::Circle{{20, 0}, 200}; + std::vector circles = {circle}; + + std::filesystem::remove(filepath); + auto bbox = task3::plot_image(filepath, lines, polylines, circles); + using namespace std::chrono_literals; + std::this_thread::sleep_for(100ms); + assert(std::filesystem::exists(filepath)); + + const auto& [bbmin, bbmax] = bbox; + + assert(bbmin[0] == -180); + assert(bbmax[0] == 220); + assert(bbmin[1] == -200); + assert(bbmax[1] == 200); + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise7/.clang-format b/exercise7/.clang-format new file mode 100644 index 0000000..0c35b1b --- /dev/null +++ b/exercise7/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise7/.clangd b/exercise7/.clangd new file mode 100644 index 0000000..55e7d7b --- /dev/null +++ b/exercise7/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: + # - --target=x86_64-w64-windows-gnu + # - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] diff --git a/exercise7/.ctests b/exercise7/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise7/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise7/.expected-files b/exercise7/.expected-files new file mode 100644 index 0000000..302e899 --- /dev/null +++ b/exercise7/.expected-files @@ -0,0 +1,3 @@ +task1.main.cpp +task2.hpp +task3.hpp diff --git a/exercise7/.gitattributes b/exercise7/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise7/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise7/.gitignore b/exercise7/.gitignore new file mode 100644 index 0000000..63d24d2 --- /dev/null +++ b/exercise7/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix diff --git a/exercise7/CMakeLists.txt b/exercise7/CMakeLists.txt new file mode 100644 index 0000000..f9df112 --- /dev/null +++ b/exercise7/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise5 LANGUAGES CXX + DESCRIPTION "exercise7" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise7") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.cpp) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION ".*360050.*electromagnetic") + + add_executable(task2 task2.test.cpp) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3 task3.test.cpp) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + diff --git a/exercise7/README.md b/exercise7/README.md new file mode 100644 index 0000000..44d237a --- /dev/null +++ b/exercise7/README.md @@ -0,0 +1,18 @@ +# Hausübung 7 (3 Punkte) + +**Ausgabe**: Donnerstag 02. Mai 2024, vormittags. + +**Abgabe bis**: Montag 13. Mai 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise7`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Arbeiten mit generischen Funktionen/Klassen (*templates*) +- Implementierung einer Klasse, die den Anforderungen eine gegebenen Template-Schnittstelle (*concept*) erfüllt + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise7/compile_flags.txt b/exercise7/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise7/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise7/main.ipynb b/exercise7/main.ipynb new file mode 100644 index 0000000..208d034 --- /dev/null +++ b/exercise7/main.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C++-Programm (*function template*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.cpp`](task1.main.cpp) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```cpp\n", + "\t#include // std::cout, std::endl\n", + "\t#include // std::vector\n", + "\t...\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion in einem eigenen Namensraum, z.B.:\n", + "\t```cpp\n", + "\tnamespace task1 {\n", + "\n", + "\t...\n", + "\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\n", + "\t ...\n", + "\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.cpp`](task1.main.cpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.cpp`](task1.main.cpp)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Umschreiben gegebener Funktionen zur Funktions-Templates (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In dem Header [`task2.hpp`](task2.hpp) sind 4 Funktionen in Namensraum `task2` gegeben.\n", + "Ihre Aufgabe besteht darin, diese so anzupassen, dass mindestens die im Test verwendeten Parametertypen unterstützt werden.\n", + "\n", + "Ihre Implementierung soll durch *verändern* der gegebenen Funktionen erfolgen: **Sie schreiben jede der Funktionen zu einem Funktions-Template um**.\n", + "\n", + "Hier ein Beispiel anhand einer Funktion `add`:" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```cpp\n", + "namespace task2 {\n", + "\n", + "/// handout\n", + "inline int add(int a, int b)\n", + "{\n", + " return a + b;\n", + "}\n", + "\n", + "/// solution\n", + "template \n", + "inline T add(T a, T b)\n", + "{\n", + " return a + b;\n", + "}\n", + "\n", + "} \n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Funktionen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.hpp`](task2.hpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task2.hpp`](task2.hpp)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.cpp`](task2.test.cpp) \n", + "- **Wichtig**: Nach erfolgter Implemenierung müssen Sie in [`task2.test.cpp`](task2.test.cpp) die Tests für varierende Parametertypen **manuell einkommentieren**." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Implementierung einer eigenen Klasse, die gegebene Schnittstellenanforderungen erfüllt (1 Punkt)\n", + "\n", + "Für diese Aufgabe implementieren Sie eine Klasse `point`, welche die Schnittstellenanforderungen (*concept*) in [`task3.concept.hpp`](task3.concept.hpp) erfüllt. " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.hpp`](task3.hpp)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task3.hpp`](task3.hpp)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.cpp`](task3.test.cpp)\n", + "- **Wichtig**: Nach erfolgter Implemenierung müssen Sie in [`task3.test.cpp`](task3.test.cpp) die Tests **manuell einkommentieren**." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `g++`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "g++ -g -std=c++20 task1.main.cpp -o build/task1\n", + "g++ -g -std=c++20 task2.test.cpp -o build/task2\n", + "g++ -g -std=c++20 task3.test.cpp -o build/task3\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\n", + "./build/task3\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 --verbose\n", + "ctest --test-dir build -C Debug -R task2 --verbose\n", + "ctest --test-dir build -C Debug -R task3 --verbose\n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise7/task1.main.cpp b/exercise7/task1.main.cpp new file mode 100644 index 0000000..78f56ae --- /dev/null +++ b/exercise7/task1.main.cpp @@ -0,0 +1,25 @@ +/// @file +/// @brief Task1: "single-file" executable C++ program + +/// @todo Include standard library headers as needed + +namespace task1 { + +/// @todo Implement a function template 'print' for a template type T according to the description below: +/// - a std::vector with elements of type T is received via a parameter (std::vector) +/// - the function then prints all elements to the console **without separation** (i.e. {1,2,3} => 123) ending with a +/// new line +/// - the function has no return value +/// @note You can assume that the template type T is printable, i.e. this works: +/// T value{}; +/// std::cout << value; + +} // namespace task1 + +/// @todo Implement a 'main' function conducting the following tasks in this order: +/// - fill a std::vector with with the following sequence of values +/// 3, 6, 0, 0, 5, 0 +/// - fill a std::vector with with the following sequence of values +/// "e", "lec", "tro", "mag", "net", "ic" +/// - use your function to print both vectors to the console separately +int main() { return 0; } diff --git a/exercise7/task2.hpp b/exercise7/task2.hpp new file mode 100644 index 0000000..78458f9 --- /dev/null +++ b/exercise7/task2.hpp @@ -0,0 +1,81 @@ +/// @file +/// @brief Task2: Function Declaration & Definitions + +#pragma once + +#include // std::size_t +#include // std::set +#include // std::vector + +namespace task2 { + +/// @brief Sums the values +/// @param vec sequence of values +/// @return Sum of all values in the sequence +/// @todo Adjust the function to accept a vector of arbitrary types: +/// - You can assume that the arbitrary type supports the += operator +/// - You need to uncomment respective lines in task2.test.cpp to test your implementation. +inline int sum(const std::vector& vec) { + int sum{}; // inits with '0' + + for (const int& x : vec) { + sum += x; + } + + return sum; +} + +/// @brief Finds the minimum value +/// @param vec Sequence of values (assume size() >= 1) +/// @return Minimum value in the sequence +/// @todo Adjust the function to accept a vector of arbitrary types: +/// - You can assume that the arbitrary type supports the '<' operator +/// - You need to uncomment respective lines in task2.test.cpp to test your implementation. +inline int min(const std::vector& vec) { + int min = vec.front(); + + for (const int& x : vec) { + if (x < min) { + min = x; + } + } + + return min; +} + +/// @brief Finds the number of unique values +/// @param vec Sequence of values +/// @return Number of unique values in the sequence +/// @todo Adjust the function to accept a vector of arbitrary types: +/// - You can assume that the arbitrary type can be inserted into a std::set (i.e. is less-compareable) +/// - You need to uncomment respective lines in task2.test.cpp to test your implementation. +inline std::size_t count_unique(const std::vector& vec) { + std::set unique; + + for (const int& x : vec) { + unique.insert(x); + } + + return unique.size(); +} + +/// @brief Generates a range of equidistant values in an interval +/// @param start Start of the interval +/// @param end End of the interval +/// @param N number of values to generate +/// @return Sequence of equidistant values +/// @todo Adjust the function to accept any type (noy only 'float'): +/// - You can assume that the arbitrary type support arithmetic operations +/// - You need to uncomment respective lines in task2.test.cpp to test your implementation. +inline std::vector range(float start, float end, unsigned int N) { + std::vector rtn; + const float step = (end - start) / (N - 1); + + for (auto n = 0u; n < N; ++n) { + rtn.emplace_back(start + step * n); + } + + return rtn; +} + +} // namespace task2 diff --git a/exercise7/task2.test.cpp b/exercise7/task2.test.cpp new file mode 100644 index 0000000..35dd171 --- /dev/null +++ b/exercise7/task2.test.cpp @@ -0,0 +1,88 @@ +/// @file +/// @brief Task2: tests + +#include "task2.hpp" // task2::sum, task2::min, ... + +#include // assert +#include // std::abs +#include // std::string +#include // std::cout, sts::endl + +///@brief task2::sum signature check: returns a T when passing a std::vector +template +concept sum_valid = requires(std::vector vec) { + { task2::sum(vec) } -> std::same_as; +}; + +///@brief task2::min signature check: returns a T when passing a std::vector +template +concept min_valid = requires(std::vector vec) { + { task2::min(vec) } -> std::same_as; +}; + +///@brief task2::count_unique signature check: returns an integer (std::size_t) when passing a std::vector +template +concept count_unique_valid = requires(std::vector vec) { + { task2::count_unique(vec) } -> std::same_as; +}; + +///@brief task2::range signature check: returns a std::vector when passing a type T as start and end +template +concept range_valid = requires(T value) { + { task2::range(value, value, 0u) } -> std::same_as>; +}; + +int main() { + + { // testing task2::sum + + // testing formal interface + static_assert(sum_valid); + // static_assert(sum_valid); // todo: uncomment + // static_assert(sum_valid); // todo: uncomment + + // testing functionality + assert((task2::sum(std::vector{1, 2, 3}) == 6)); + // assert(std::abs(task2::sum(std::vector{0.1, 0.2, 0.3}) - 0.6) < 1e-9); // todo: uncomment + // assert(std::abs(task2::sum(std::vector{0.1f, 0.2f, 0.3f}) - 0.6f) < 1e-6f); // todo: uncomment + } + + { // testing task2::min + + // testing formal interface + static_assert(min_valid); + // static_assert(min_valid); // todo: uncomment + // static_assert(min_valid); // todo: uncomment + + // testing functionality + assert((task2::min(std::vector{1, 2, 3}) == 1)); + // assert(std::abs(task2::min(std::vector{0.1, 0.2, 0.3}) - 0.1) < 1e-9); // todo: uncomment + // assert(std::abs(task2::min(std::vector{0.1f, 0.2f, 0.3f}) - 0.1f) < 1e-6f); // todo: uncomment + } + + { // testing task2::count_unique + + // testing formal interface + static_assert(count_unique_valid); + // static_assert(count_unique_valid); // todo: uncomment + + // testing functionality + assert(task2::count_unique(std::vector{1, 2, 3}) == 3); + // assert(task2::count_unique(std::vector{{"one", "one", "two"}}) == 2); // todo: uncomment + } + + { // testing task2::range + + // testing formal interface + static_assert(range_valid); + // static_assert(range_valid); // todo: uncomment + + // testing functionality + assert(std::abs(task2::range(0.0f, 10.0f, 3).at(1) - 5.0f) < 1e-6); + // assert(std::abs(task2::range(0.0, 10.0, 3).at(1) - 5.0) < 1e-9); // todo: uncomment + } + + std::cout << "task2.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise7/task3.concept.hpp b/exercise7/task3.concept.hpp new file mode 100644 index 0000000..b02ba0d --- /dev/null +++ b/exercise7/task3.concept.hpp @@ -0,0 +1,24 @@ +/// @file +/// @brief Task3: Formal interface requirements + +#pragma once + +#include // std::same_as + +namespace task3 { + +/// @brief Formal interface requirements (concept) for your 'Point' class +template +concept PointConcept = requires(T value, const T& const_ref, const T& const_other) { + { T(10, 20) }; ///< T can be constructed from two numerical values + + { const_ref.get_x() } -> std::same_as; ///< T has const public member function `get_x` that returns `double` + { const_ref.get_y() } -> std::same_as; ///< T has const public member function `get_y` that returns `double` + + { value.set_x(10) }; ///< T has public member function `set_x` that accepts number + { value.set_y(10) }; ///< T has public member function `set_y` that accepts number + + { const_ref == const_other } -> std::same_as; ///< T has const operator==() overload for another T that returns boolean. +}; + +} // namespace task3 diff --git a/exercise7/task3.hpp b/exercise7/task3.hpp new file mode 100644 index 0000000..363ddd0 --- /dev/null +++ b/exercise7/task3.hpp @@ -0,0 +1,10 @@ +/// @file +/// @brief Task3: Implementation of 'Point' + +#pragma once + +namespace task3 { + +/// @todo Implement a struct or class 'Point' that fulfills the concept task3::PointConcept (details see 'task3.concept.hpp') + +} // namespace task3 diff --git a/exercise7/task3.test.cpp b/exercise7/task3.test.cpp new file mode 100644 index 0000000..781e160 --- /dev/null +++ b/exercise7/task3.test.cpp @@ -0,0 +1,17 @@ +/// @file +/// @brief Task3: tests + +#include "task3.hpp" // task3::Point +#include "task3.concept.hpp" // task3::PointConcept +#include // std::cout, sts::endl + +int main() { + + using namespace task3; + + // static_assert(PointConcept); // todo: uncomment + + std::cout << "task3.test.cpp: all asserts passed" << std::endl; + + return 0; +} diff --git a/exercise8/.clang-format b/exercise8/.clang-format new file mode 100644 index 0000000..0c35b1b --- /dev/null +++ b/exercise8/.clang-format @@ -0,0 +1,10 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/exercise8/.clangd b/exercise8/.clangd new file mode 100644 index 0000000..55e7d7b --- /dev/null +++ b/exercise8/.clangd @@ -0,0 +1,25 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: + # - --target=x86_64-w64-windows-gnu + # - --target=x86_64-pc-linux-gnu + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] diff --git a/exercise8/.ctests b/exercise8/.ctests new file mode 100644 index 0000000..b1d1190 --- /dev/null +++ b/exercise8/.ctests @@ -0,0 +1,3 @@ +task1 +task2 +task3 diff --git a/exercise8/.expected-files b/exercise8/.expected-files new file mode 100644 index 0000000..c8bc521 --- /dev/null +++ b/exercise8/.expected-files @@ -0,0 +1,3 @@ +task1.main.c +task2.c +task3.c diff --git a/exercise8/.gitattributes b/exercise8/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/exercise8/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/exercise8/.gitignore b/exercise8/.gitignore new file mode 100644 index 0000000..63d24d2 --- /dev/null +++ b/exercise8/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix diff --git a/exercise8/.gitmodules b/exercise8/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/exercise8/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/exercise8/CMakeLists.txt b/exercise8/CMakeLists.txt new file mode 100644 index 0000000..ed95f26 --- /dev/null +++ b/exercise8/CMakeLists.txt @@ -0,0 +1,59 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(exercise8 LANGUAGES C + DESCRIPTION "exercise8" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/exercise8") + +# setting required language standards + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED True) + set(CMAKE_C_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# find math library and link to all targets + + find_library(MATH_LIBRARY m) + link_libraries(${MATH_LIBRARY}) + +# get/setup dependencies + + include_directories(modules) + +# include own targets + + add_executable(task1 task1.main.c + task1.main.c) + target_link_libraries(task1 PRIVATE ${MATH_LIBRARY}) + add_test(NAME task1 COMMAND task1 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + set_property(TEST task1 PROPERTY PASS_REGULAR_EXPRESSION "8") + + add_executable(task2 task2.c task2.test.c + task2.test.c + task2.h + task2.c) + target_link_libraries(task2 PRIVATE ${MATH_LIBRARY}) + add_test(NAME task2 COMMAND task2 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(task3 task2.c task3.c task3.test.c) + target_link_libraries(task3 PRIVATE ${MATH_LIBRARY}) + add_test(NAME task3 COMMAND task3 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + diff --git a/exercise8/README.md b/exercise8/README.md new file mode 100644 index 0000000..de9814c --- /dev/null +++ b/exercise8/README.md @@ -0,0 +1,19 @@ +# Hausübung 8 (3 Punkte) + +**Ausgabe**: Donnerstag 16. Mai 2024, vormittags. + +**Abgabe bis**: Montag 27. Mai 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise8`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- Sprache C: Kompilierbefehle +- Sprache C: Strukturen und Funktionen +- Sprache C: Übergabe von Feldern an Funktionen + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise8/compile_flags.txt b/exercise8/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/exercise8/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/exercise8/main.ipynb b/exercise8/main.ipynb new file mode 100644 index 0000000..4ebcba9 --- /dev/null +++ b/exercise8/main.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C-Programm (*array as function parameter*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.c`](task1.main.c) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```c\n", + "\t#include // bool, true, false\n", + "\t#include // printf\n", + "\t...\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion, z.B.:\n", + "\t```cpp\n", + "\tint task1_func(...) {\n", + "\t ...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\t\n", + "\t int res = task1_func(...);\n", + "\t ...\t\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.c`](task1.main.c)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.c`](task1.main.c)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Reimplementierung von C++ Funktionalität in C (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sie reimplementieren die Funktionalität zweier Klassen, die Sie schon in Hausübung 4 (C++) implementiert:\n", + "\n", + "- [`exercise4::task2::BBox`](https://sgit.iue.tuwien.ac.at/360050/exercise4/src/commit/2e408ac941971e07971485282ef086d22cb0d974/task2.hpp#L13) \n", + "- [`exercise4::task2::Circle`](https://sgit.iue.tuwien.ac.at/360050/exercise4/src/commit/2e408ac941971e07971485282ef086d22cb0d974/task2.hpp#L35)\n", + "\n", + "Diesmal verwenden Sie die Sprache C:\n", + "\n", + "- Statt Memberfunktionen kommen freie Funktionen zum Einsatz\n", + "- Funktionsüberladungen werden durch Postfixe/Präfixe bei den Funktionsnamen realisiert\n", + "- Typen aus der C++ Standardbibliothek (hier z.B. `std::array` ) werden durch eigene Typen ersetzt\n", + "- Funktionalität aus der C++ Standardbibliothek (z.B. `std::cout`) wird mit entsprechender Funktionalität aus der C Standardbibliothek ( `printf` ) ersetzt." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Strukturen/Funktionen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.h`](task2.h)\n", + "- Ihre Implementierung erfolgt in [`task2.c`](task2.c)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.c`](task2.test.c) " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Übergabe von Feldern von Strukturen an Funktionen (1 Punkt)\n", + "\n", + "Sie implementieren zwei Funktionen an die ein Feld von Strukturen (aus Aufgabe 2) übergeben wird:" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "c" + } + }, + "source": [ + "```c\n", + "// todo: implement\n", + "unsigned int task3_count_inside(const struct BBox* box, const struct Circle circles[], unsigned int size);\n", + "\n", + "// todo: implement\n", + "struct BBox task3_common_bbox(const struct Circle circles[], unsigned int size);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.h`](task3.h)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task3.c`](task3.c)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.c`](task3.test.c)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `gcc`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "gcc -g -std=c11 task1.main.c -o build/task1 -lm\n", + "gcc -g -Imodules -std=c11 task2.c task2.test.c -o build/task2 -lm\n", + "gcc -g -Imodules -std=c11 task2.c task3.c task3.test.c -o build/task3 -lm\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\n", + "./build/task3\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 --verbose\n", + "ctest --test-dir build -C Debug -R task2 --verbose\n", + "ctest --test-dir build -C Debug -R task3 --verbose\n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise8/modules b/exercise8/modules new file mode 160000 index 0000000..63b4f08 --- /dev/null +++ b/exercise8/modules @@ -0,0 +1 @@ +Subproject commit 63b4f080520402cc41061d0eea998e854e4d0a95 diff --git a/exercise8/task1.main.c b/exercise8/task1.main.c new file mode 100644 index 0000000..90af8bc --- /dev/null +++ b/exercise8/task1.main.c @@ -0,0 +1,44 @@ +#include +#include + +bool isprime(unsigned int num) { + // Handle edge cases (0, 1, and even numbers greater than 2) + if (num <= 1 || num % 2 == 0 && num > 2) { + return false; + } + + // Efficiently check divisibility only up to the square root of num + for (unsigned int i = 3; i * i <= num; i += 2) { + if (num % i == 0) { + return false; + } + } + return true; +} + +int count_primes(unsigned int values[], int size) { + int count = 0; + for (int i = 0; i < size; i++) { + if (isprime(values[i])) { + count++; + } + } + return count; +} + +int main() { + unsigned int arr1[] = {1, 20, 21, 43, 32, 42, 2344, 42433, 6, 2, 0, 22, 45, 47, 1033, 1247}; + int size1 = sizeof(arr1) / sizeof(arr1[0]); + + unsigned int arr2[] = {21, 22, 44, 33, 7, 3, 1, 23, 46, 48, 1034, 1248}; + int size2 = sizeof(arr2) / sizeof(arr2[0]); + + int primes1 = count_primes(arr1, size1); + int primes2 = count_primes(arr2, size2); + + int total_primes = primes1 + primes2; + + printf("Total number of primes: %d\n", total_primes); + + return 0; +} diff --git a/exercise8/task2.c b/exercise8/task2.c new file mode 100644 index 0000000..8ac3f38 --- /dev/null +++ b/exercise8/task2.c @@ -0,0 +1,52 @@ +/// @file +/// @brief Task2: function definitions + +#include "task2.h" // struct Matrix, matrix_mult + +#include // size_t +#include // printf +#include // malloc, free + +/// @todo Include C standard library headers as needed + +/// @note This implementation is provided as declared and specified in task2.h +struct Matrix matrix_init(size_t m, size_t n, const double* data) { + struct Matrix res = {.data = malloc(sizeof(double) * m * n), .m = m, .n = n}; + double* A = res.data; + for (size_t i = 0; i != m * n; ++i) + A[i] = data[i]; + return res; +} + +/// @note This implementation is provided as declared and specified in task2.h +void matrix_print(const struct Matrix* mat) { + size_t M = mat->m; + size_t N = mat->n; + const double* A = mat->data; + + for (size_t m = 0; m != M; ++m) { + for (size_t n = 0; n != N; ++n) + printf("%lf ", A[n + N * m]); + printf("\n"); + } + printf("\n"); +} + +/// @note This implementation is provided as declared and specified in task2.h +void matrix_clear(struct Matrix* mat) { + free(mat->data); + mat->m = 0; + mat->n = 0; +} + +/// @todo Implement function 'matrix_zeros' as declared and specified in task2.h +struct Matrix matrix_zeros(size_t m, size_t n) { return (struct Matrix){.data = NULL, .m = 0, .n = 0}; } + +/// @todo Implement function 'matrix_identity' as declared and specified in task2.h +struct Matrix matrix_identity(size_t n) { return (struct Matrix){.data = NULL, .m = 0, .n = 0}; } + +/// @todo Implement function 'matrix_transpose' as declared and specified in task2.h +void matrix_transpose(struct Matrix* a) {} + +/// @todo Implement function 'matrix_mult' as declared and specified in task2.h +void matrix_mult(const struct Matrix* a, const struct Matrix* b, struct Matrix* c) {} diff --git a/exercise8/task2.h b/exercise8/task2.h new file mode 100644 index 0000000..b5be830 --- /dev/null +++ b/exercise8/task2.h @@ -0,0 +1,51 @@ +/// @file +/// @brief Task2: Structure definitions and function declarations + +#pragma once + +#include // size_t + +/// @brief Two-dimensional matrix with 'm' rows and 'n' columns. +/// @note: the values ares stored in a contiguous block in memory in row-major layout +/// @note: row-major storage order is used, access of element (i,j) -> data[j + n*i] +struct Matrix { + double* data; ///< pointer to a dynamically allocated contiguous memory block fitting m*n values + size_t m; ///< number of rows (first dimension) + size_t n; ///< number of columns (second dimension) +}; + +/// @brief Initalize a matrix from the values in a buffer +/// @param m first dimension of the matrix +/// @param n second dimension of the matrix +/// @param data contiguous memory holding the values to copy (in row-major format) +struct Matrix matrix_init(size_t m, size_t n, const double* data); + +/// @brief Prints a Matrix to the console +/// @param mat Matrix to be printed +void matrix_print(const struct Matrix* mat); + +/// @brief Resets a matrix (deallocates memory and sets its size to 0 x 0) +/// @param mat Matrix to be reset +void matrix_clear(struct Matrix* mat); + +/// @brief Initializes an matrix with zeros +/// @param m first dimension of the matrix +/// @param n first dimension of the matrix +struct Matrix matrix_zeros(size_t m, size_t n); + +/// @brief Initializes a square identity matrix +/// @param n dimension of the identity matrix +struct Matrix matrix_identity(size_t n); + +/// @brief Transposes a matrix +/// @param mat Matrix to be transposed +/// @note this function might swap/replace the block of memory owned by the matrix +void matrix_transpose(struct Matrix* mat); + +/// @brief Performs a matrix-matrix mutliplication: a*b = c +/// @a first matrix (left factor) +/// @b second matrix (right factor) +/// @c Result of the multiplication is stored in this Matrix: +/// - the dimensions of this matrix must be 'a.m x b.n' when calling this function +/// - the values will be overwritten with the result of the multiplication +void matrix_mult(const struct Matrix* a, const struct Matrix* b, struct Matrix* c); diff --git a/exercise8/task2.test.c b/exercise8/task2.test.c new file mode 100644 index 0000000..bd885f1 --- /dev/null +++ b/exercise8/task2.test.c @@ -0,0 +1,220 @@ +/// @file +/// @brief Task2: tests + +#include "task2.h" // struct Matrix, matrix_mult + +#include "iue-num/numerics.h" // iuenum_isclose + +#include // assert +#include // bool, true, false +#include // printf + +// helper function +bool isclose(const struct Matrix* a, const struct Matrix* b) { + if (a->m != b->m) + return false; + if (a->n != b->n) + return false; + for (size_t i = 0; i != a->m; ++i) + for (size_t j = 0; j != a->n; ++j) + if (!iuenum_isclose(a->data[j + a->n * i], b->data[j + b->n * i])) + return false; + return true; +} + +int main() { + + { // testing 'matrix_zeros' for 3x3 + struct Matrix mat = matrix_zeros(3, 3); + double data[3][3] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; + struct Matrix expected = matrix_init(3, 3, &data[0][0]); + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_zeros' for 4x3 + struct Matrix mat = matrix_zeros(4, 3); + double data[4][3] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; + struct Matrix expected = matrix_init(4, 3, &data[0][0]); + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_identity' for 3x3 + + double data_expected[3][3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }; + struct Matrix expected = matrix_init(3, 3, &data_expected[0][0]); + struct Matrix mat = matrix_identity(3); + + // matrix_print(&mat); + assert(isclose(&mat, &expected)); + // matrix_print(&expected); + + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_identity' for 4x4 + + double data_expected[4][4] = { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1}, + }; + struct Matrix expected = matrix_init(4, 4, &data_expected[0][0]); + struct Matrix mat = matrix_identity(4); + + // matrix_print(&mat); + assert(isclose(&mat, &expected)); + // matrix_print(&expected); + + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_transpose' of a 3x3 identity + + struct Matrix mat = matrix_identity(3); + + double data_expected[3][3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }; + struct Matrix expected = matrix_init(3, 3, &data_expected[0][0]); + + // matrix_print(&mat); + matrix_transpose(&mat); + // matrix_print(&mat); + // matrix_print(&expected); + + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_transpose' for 4x2 + + double data[4][2] = { + {11, 12}, + {21, 22}, + {31, 32}, + {41, 42}, + }; + struct Matrix mat = matrix_init(4, 2, &data[0][0]); + + double data_expected[2][4] = { + {11, 21, 31, 41}, + {12, 22, 32, 42}, + }; + struct Matrix expected = matrix_init(2, 4, &data_expected[0][0]); + + // matrix_print(&mat); + matrix_transpose(&mat); + // matrix_print(&mat); + // matrix_print(&expected); + + assert(isclose(&mat, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + } + + { // testing 'matrix_mult' using a left multiplty with a row permuting matrix + + double data[4][2] = { + {11, 12}, + {21, 22}, + {31, 32}, + {41, 42}, + }; + struct Matrix mat = matrix_init(4, 2, &data[0][0]); + + double data_permute[4][4] = { + {0, 0, 0, 1}, + {0, 0, 1, 0}, + {0, 1, 0, 0}, + {1, 0, 0, 0}, + }; + struct Matrix permute = matrix_init(4, 4, &data_permute[0][0]); + + double data_expected[4][2] = { + {41, 42}, + {31, 32}, + {21, 22}, + {11, 12}, + }; + struct Matrix expected = matrix_init(4, 2, &data_expected[0][0]); + + struct Matrix product = matrix_zeros(permute.m, mat.n); + + // matrix_print(&permute); + // matrix_print(&mat); + matrix_mult(&permute, &mat, &product); // left multiply with permuation matrix + // matrix_print(&product); + + assert(isclose(&product, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + matrix_clear(&permute); + matrix_clear(&product); + } + + + { // testing 'matrix_mult' using a right multiplty with a column permuting matrix + + double data[4][2] = { + {11, 12}, + {21, 22}, + {31, 32}, + {41, 42}, + }; + struct Matrix mat = matrix_init(4, 2, &data[0][0]); + + double data_permute[2][2] = { + {0, 1}, + {1, 0}, + }; + struct Matrix permute = matrix_init(2, 2, &data_permute[0][0]); + + double data_expected[4][2] = { + {12, 11}, + {22, 21}, + {32, 31}, + {42, 41}, + }; + struct Matrix expected = matrix_init(4, 2, &data_expected[0][0]); + + struct Matrix product = matrix_zeros(mat.m, permute.n); + + // matrix_print(&permute); + // matrix_print(&mat); + matrix_mult(&mat, &permute, &product); // right multiply with permuation matrix + // matrix_print(&product); + + assert(isclose(&product, &expected)); + matrix_clear(&mat); + matrix_clear(&expected); + matrix_clear(&permute); + matrix_clear(&product); + } + + printf("task2.test.c: all asserts passed\n"); + + return 0; +} diff --git a/exercise8/task3.c b/exercise8/task3.c new file mode 100644 index 0000000..7f38d1c --- /dev/null +++ b/exercise8/task3.c @@ -0,0 +1,54 @@ +#include "task3.h" // task3_count_inside, task3_common_bbox +#include "task2.h" // struct Vec2d, struct BBox, struct Circle +#include // for bool (true/false) + +/// @brief Count the number of Circles fully contained inside a bounding box +/// @param box Bounding box +/// @param circles Array of Circles +/// @param size Length of the 'circles' array +/// @return Number of circles fully contained inside 'box' +unsigned int task3_count_inside(const struct BBox* box, const struct Circle circles[], unsigned int size) { + unsigned int count = 0; + for (unsigned int i = 0; i < size; i++) { + const struct Circle* circle = &circles[i]; + // Check if circle center is within box boundaries + bool inside_x = (circle->c.x - circle->r >= box->min.x) && (circle->c.x + circle->r <= box->max.x); + bool inside_y = (circle->c.y - circle->r >= box->min.y) && (circle->c.y + circle->r <= box->max.y); + if (inside_x && inside_y) { + count++; + } + } + return count; +} + +/// @brief Generates the smallest bounding box containing a sequence of circles +/// @param circles Array of Circles +/// @param size Length of the 'circles' array (Assertion: size >= 1) +/// @return Smallest bounding box containing all circles +struct BBox task3_common_bbox(const struct Circle circles[], unsigned int size) { + struct BBox res; + // Initialize with first circle's data (assuming at least one circle) + res.min.x = circles[0].c.x - circles[0].r; + res.min.y = circles[0].c.y - circles[0].r; + res.max.x = circles[0].c.x + circles[0].r; + res.max.y = circles[0].c.y + circles[0].r; + + for (unsigned int i = 1; i < size; i++) { + const struct Circle* circle = &circles[i]; + // Update minimum bounds if circle's center falls outside current box + if (circle->c.x - circle->r < res.min.x) { + res.min.x = circle->c.x - circle->r; + } + if (circle->c.y - circle->r < res.min.y) { + res.min.y = circle->c.y - circle->r; + } + // Update maximum bounds if circle's edge falls outside current box + if (circle->c.x + circle->r > res.max.x) { + res.max.x = circle->c.x + circle->r; + } + if (circle->c.y + circle->r > res.max.y) { + res.max.y = circle->c.y + circle->r; + } + } + return res; +} diff --git a/exercise8/task3.h b/exercise8/task3.h new file mode 100644 index 0000000..c3d0e69 --- /dev/null +++ b/exercise8/task3.h @@ -0,0 +1,19 @@ +/// @file +/// @brief Task3: structure and function declarations + +#pragma once + +#include "task2.h" // struct Vec2d, struct BBox, struct Circle + +/// @brief Count the number of Circles fully contained inside a bounding box +/// @param box Bounding box +/// @param circles Array of Circles +/// @param size Length of the 'circles' array +/// @return Number of circles fully contained inside 'box' +unsigned int task3_count_inside(const struct BBox* box, const struct Circle circles[], unsigned int size); + +/// @brief Generates the smallest bounding box containing a sequence of circles +/// @param circles Array of Circles +/// @param size Length of the 'circles' array (Assertion: size >= 1) +/// @return Smallest bounding box containing all circles +struct BBox task3_common_bbox(const struct Circle circles[], unsigned int size); diff --git a/exercise8/task3.test.c b/exercise8/task3.test.c new file mode 100644 index 0000000..db434ec --- /dev/null +++ b/exercise8/task3.test.c @@ -0,0 +1,68 @@ +/// @file +/// @brief Task3: tests + +#include "task3.h" // task3_count_inside, task3_common_bbox +#include "task2.h" // struct Vec2d, struct BBox, struct Cirlce + +#include "iue-num/numerics.h" // iuenum_isclose + +#include // assert +#include // printf + +bool detail_isclose_vec2d(struct Vec2d a, struct Vec2d b) { + if (!iuenum_isclose(a.x, b.x)) + return false; + if (!iuenum_isclose(a.y, b.y)) + return false; + return true; +} + +bool detail_isclose_bbox(const struct BBox* a, const struct BBox* b) { + if (!detail_isclose_vec2d(a->min, b->min)) + return false; + if (!detail_isclose_vec2d(a->max, b->max)) + return false; + return true; +} + +int main() { + + { // testing 'task3_count_inside' + struct Vec2d min = {-1.6, -0.6}; + struct Vec2d max = {3.6, 2.6}; + struct BBox box = {min, max}; + + struct Circle c1 = {{0.0, 1.0}, 1.5}; + struct Circle c2 = {{1.0, 1.0}, 1.5}; + struct Circle c3 = {{2.0, 1.0}, 1.5}; + struct Circle c4 = {{3.0, 1.0}, 1.5}; + struct Circle c5 = {{4.0, 1.0}, 1.5}; + + struct Circle circles[5] = {c1, c2, c3, c4, c5}; + + unsigned int res = task3_count_inside(&box, circles, 5); + + assert(res == 3); + } + + { // testing 'task3_common_bbox' + + struct Circle c1 = {{0.0, 1.0}, 1.5}; + struct Circle c2 = {{1.0, 1.0}, 1.5}; + struct Circle c3 = {{2.0, 1.0}, 1.5}; + + struct Circle circles[3] = {c1, c2, c3}; + + struct BBox res = task3_common_bbox(circles, 3); + + struct Vec2d exp_min = {-1.5, -0.5}; + struct Vec2d exp_max = {3.5, 2.5}; + struct BBox exp_box = {exp_min, exp_max}; + + assert(detail_isclose_bbox(&res,&exp_box)); + } + + printf("task3.test.c: all asserts passed\n"); + + return 0; +} diff --git a/exercise9/.idea/.gitignore b/exercise9/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/exercise9/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/exercise9/.idea/exercise9.iml b/exercise9/.idea/exercise9.iml new file mode 100644 index 0000000..bc2cd87 --- /dev/null +++ b/exercise9/.idea/exercise9.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/exercise9/.idea/modules.xml b/exercise9/.idea/modules.xml new file mode 100644 index 0000000..f79aa59 --- /dev/null +++ b/exercise9/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/exercise9/.idea/vcs.xml b/exercise9/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/exercise9/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/exercise9/README.md b/exercise9/README.md new file mode 100644 index 0000000..533d671 --- /dev/null +++ b/exercise9/README.md @@ -0,0 +1,19 @@ +# Hausübung 9 (3 Punkte) + +**Ausgabe**: Donnerstag 23. Mai 2024, vormittags. + +**Abgabe bis**: Montag 3. Juni 2024, Ende des Tages. + +**Abgabe via**: git-Repository mit dem Namen **`exercise9`** auf unserem git-Server https://sgit.iue.tuwien.ac.at + +Details zum Abgabeprozess via `git` finden Sie hier: https://sgit.iue.tuwien.ac.at/360050/git + +# Aufgabenstellung + +In dieser Hausübung werden folgende Themen erstmalig einfliessen: + +- C: Nullterminierte Zeichenketten +- C: Lexikographischer Vergleich von Zeichenketten +- C: Kopieren von Zeichenketten + +**Die genaue Beschreibung und Anforderungen finden Sie in [`main.ipynb`](main.ipynb) und im Quellcode.** diff --git a/exercise9/main.ipynb b/exercise9/main.ipynb new file mode 100644 index 0000000..f3a5137 --- /dev/null +++ b/exercise9/main.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 1: Ein eigenes kleines C-Programm (*count spaces in string*) (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Erstellen Sie in [`task1.main.c`](task1.main.c) ein lauffähiges Ein-Dateien-Programm das folgende Struktur aufweist:\n", + "\n", + "- Einbinden benötigter Header-Dateien aus der Standardbibliothek, z.B.:\n", + "\t```c\n", + "\t#include // isspace\n", + "\t#include // printf\n", + "\t#include // size_t\t\n", + "\t...\n", + "\t```\n", + "- Definition/Implementierung einer eigenen Funktion, z.B.:\n", + "\t```cpp\n", + "\tsize_t func(...) {\n", + "\t ...\n", + "\t}\n", + "\t``` \n", + "- Definition/Implementierung einer `main`-Funktion, die Ihre selbst geschriebene Funktion verwendet, z.B.:\n", + "\t```cpp\n", + "\tint main(){\n", + "\t ...\t\n", + "\t size_t res = func(...);\n", + "\t ...\t\n", + "\t return 0;\n", + "\t}\n", + "\t``` \n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Eine genaue Beschreibung und Anforderungen finden Sie in [`task1.main.c`](task1.main.c)\n", + "- Ihre Implementierung erfolgt ebenfalls in [`task1.main.c`](task1.main.c)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 2: Vergleichen und Kopieren von nullterminierten Zeichenketten (1 Punkt)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "**Wichtig:** Sie duerfen fuer Aufgabe 2 **keine** Funktionalitaet aus der Header-Datei `` (Standardbibliothek) verwenden. Hintergrund: die Funktionen, die Sie implementieren sind stark an Funktionen aus `` angelehnt (`strcpy/strncpy` und `strcmp/strncmp`)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sie implementieren zwei Funktionen, die nullterminierte Zeichenketten lexikographisch vergleichen. Lexikographisch bedeutet hier: \n", + "- die Sequenzen werden Zeichen-für-Zeichen verglichen\n", + "- ein kleinerer Wert liegt lexikographisch vor einem grösseren Wert\n", + "- Bei der Kontrolle/Debuggen mittels Ausgabe in der Konsole ist die Zuordnung der Zeichen (`char`) mittels der [ASCII-Tabelle](https://en.wikipedia.org/wiki/ASCII#Character_set) zu beachten; es ergibt sich u.A. folgendes:\n", + "\t- Das Terminierungszeichen (`\\0`) kommt vor allen anderen Zeichen\n", + "\t- Zahlen kommen vor Buchstaben\n", + "\t- Grosse Buchstabe kommen vor kleinen Buchstaben\n", + "\t- usw.\n", + "\n", + "```c\n", + "// todo: implement\n", + "int task2_compare(const char* a, const char* b);\n", + "// todo: implement\n", + "int task2_compare_n(const char* a, const char* b, size_t count);\n", + "```\n", + "\n", + "Sie implementieren zwei weitere Funktionen, die nullterminierte Zeichenketten kopieren.\n", + "\n", + "```c\n", + "// todo: implement\n", + "char* task2_copy(char* dest, const char* src);\n", + "// todo: implement\n", + "char* task2_copy_n(char* dest, const char* src, size_t count);\n", + "```\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Strukturen/Funktionen und eine genaue Beschreibung und Anforderungen finden Sie in [`task2.h`](task2.h)\n", + "- Ihre Implementierung erfolgt in [`task2.c`](task2.c)\n", + "- Die zugeordneten Tests finden Sie in [`task2.test.c`](task2.test.c) " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aufgabe 3: Analyse einer mehrzeiligen Zeichenkette (1 Punkt)\n", + "\n", + "Sie implementieren eine Funktionen, die eine nullterminierte mehrzeilige Zeichenkette analysiert:\n", + "- wieviele Zeilen gibt es?\n", + "- wieviele Wörter kommen vor?" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "c" + } + }, + "source": [ + "```c\n", + "struct StringStats {\n", + " unsigned int lines;\n", + " unsigned int words;\n", + "};\n", + "\n", + "// todo: implement\n", + "struct StringStats task3_stringstats(const char* str);\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Die vorgegebenen Deklaration und eine genaue Beschreibung und Anforderungen finden Sie in [`task3.h`](task3.h)\n", + "- Ihre Implementierung erfolgt in [`task3.c`](task3.c)\n", + "- Die zugeordneten Tests finden Sie in [`task3.test.c`](task3.test.c)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kompilieren/Testen\n", + "\n", + "So testen Sie Ihre Implementierung (direkter Aufruf von `gcc`):\n", + "\n", + "```shell\n", + "# prepare\n", + "mkdir build\n", + "# compile\n", + "gcc -g -std=c11 task1.main.c -o build/task1 -lm\n", + "gcc -g -Imodules -std=c11 task2.c task2.test.c -o build/task2 -lm\n", + "gcc -g -Imodules -std=c11 task3.c task3.test.c -o build/task3 -lm\n", + "\n", + "# run tests\n", + "./build/task1\n", + "./build/task2\n", + "./build/task3\n", + "```\n", + "\n", + "Alternativ (mittels CMake-Configuration):\n", + "\n", + "```shell\n", + "# prepare\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug\n", + "# compile\n", + "cmake --build build --config Debug --target task1\n", + "cmake --build build --config Debug --target task2\n", + "cmake --build build --config Debug --target task3\n", + "cmake --build build --config Debug # all\n", + "# run tests\n", + "ctest --test-dir build -C Debug -R task1 --verbose\n", + "ctest --test-dir build -C Debug -R task2 --verbose\n", + "ctest --test-dir build -C Debug -R task3 --verbose\n", + "ctest --test-dir build -C Debug # all\n", + "``` \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/exercise9/task1.main.c b/exercise9/task1.main.c new file mode 100644 index 0000000..7acbaf8 --- /dev/null +++ b/exercise9/task1.main.c @@ -0,0 +1,18 @@ +/// @file +/// @brief Task1: "single-file" executable C program + +/// @todo Include C standard library headers as needed + +/// @todo Implement a function 'count_spaces' according to the description below: +/// The function +/// - receives an pointer to a null-terminated charcater string, +/// - uses the function 'isspace' from to count the number of spaces, and +/// - returns the result as an unsigned integer value + +/// @todo Implement a 'main' function conducting the following tasks in this order: +/// - construct two local variables containing the following null-terminated string literals: +/// "How many spaces does this string contain?" +/// " How about this\n multiline string? " +/// - use your function 'count_spaces' to count the number of spaces in each of the string literals +/// - print both results to the console +int main() { return 0; } diff --git a/exercise9/task2.c b/exercise9/task2.c new file mode 100644 index 0000000..7895fe4 --- /dev/null +++ b/exercise9/task2.c @@ -0,0 +1,22 @@ +/// @file +/// @brief Task2: function definitions + +#include "task2.h" // task2_copy, task2_copy_n, task2_compare, task2_compare_n + +#include // size_t + +/// @todo Include C standard library headers as needed, but note that +/// You are **not** allowed to use functionality from the header +#include // NULL + +/// @todo Implement function 'task2_compare' as declared and specified in task2.h +int task2_compare(const char* a, const char* b) { return 0; } + +/// @todo Implement function 'task2_compare_n' as declared and specified in task2.h +int task2_compare_n(const char* a, const char* b, size_t count) { return 0; } + +/// @todo Implement function 'task2_copy' as declared and specified in task2.h +char* task2_copy(char* dest, const char* src) { return NULL; } + +/// @todo Implement function 'task2_copy_n' as declared and specified in task2.h +char* task2_copy_n(char* dest, const char* src, size_t count) { return NULL; } diff --git a/exercise9/task2.h b/exercise9/task2.h new file mode 100644 index 0000000..b9065b9 --- /dev/null +++ b/exercise9/task2.h @@ -0,0 +1,60 @@ +/// @file +/// @brief Task2: Structure definitions and function declarations + +#pragma once + +#include // size_t + +/// @brief Compares two strings 'a' and 'b' lexicographically, byte-by-byte +/// until the first occurence of a null termination. +/// Note: The null termination character is part of the comparison +/// @param a Pointer to the first character range +/// @param b Pointer to the second character range +/// @return The function returns +/// - a negative value, if 'a' is lexicographically ordered before 'b', +/// - a positive value, if 'a' is lexicographically ordered after 'b', +/// - zero, if 'a' and 'b' are equal +/// @note This function requires: +/// - 'a' and 'b' point to null-terminated ranges +int task2_compare(const char* a, const char* b); + +/// @brief Compares two strings 'a' and 'b' lexicographically, byte-by-byte until +/// - the occurence of a null termination, but at most +/// - up to the preset maximum 'count' of characters to be compare +/// Note: The null termination character is part of the comparison +/// @param a Pointer to the first character range +/// @param b Pointer to the second character range +/// @param count Maximum number of characters to be compared +/// @return The function returns +/// - a negative value, if 'a' is lexicographically ordered before 'b', +/// - a positive value, if 'a' is lexicographically ordered after 'b', +/// - zero, if 'a' and 'b' are equal +/// @note This function requires: +/// - 'a' and 'b' point to a null-terminated ranges +int task2_compare_n(const char* a, const char* b, size_t count); + +/// @brief Copies a string until the first occurence of a null termination. +/// from a source 'src' to destination 'dest' location +/// @param dest Pointer to the first character of the destination range +/// @param src Pointer to the first character of the source range +/// @return Pointer to the first character of the destination range +/// @note This function requires: +/// - source and destination ranges do not overlap +/// - the source points to a null-terminated range +/// - the destination range fits the size of the source range (including a null termination character) +char* task2_copy(char* dest, const char* src); + +/// @brief Copies a string from a source 'src' to destination 'dest' range: +/// - If the preset maximum 'count' of characters to be copied is reached before a null termination occurs in 'src' the +/// resulting destination range will not be null terminated, else +/// - the termination character is also placed in the destination range. +/// @param dest Pointer to the first character of the destination range +/// @param src Pointer to the first character of the source range +/// @param count Maximum number of characters to be copied +/// @return Pointer to the first character of the destination range +/// @note This function requires: +/// - source and destination ranges do not overlap +/// - the source points to a null-terminated range +/// - the destination range can fit 'count' characters +char* task2_copy_n(char* dest, const char* src, size_t count); + diff --git a/exercise9/task2.test.c b/exercise9/task2.test.c new file mode 100644 index 0000000..786c4b4 --- /dev/null +++ b/exercise9/task2.test.c @@ -0,0 +1,126 @@ +/// @file +/// @brief Task2: tests + +#include "task2.h" // task2_copy, task2_copy_n, task2_compare, task2_compare_n + +#include // assert +#include // printf + +int main() { + + // testing 'task2_compare' + + { + char a[] = "test"; + char b[] = "test"; + int res = task2_compare(a, b); + + assert(res == 0); // fails if: equal strings do not return 0 + } + { + char a[] = "test12"; + char b[] = "test22"; + int res = task2_compare(a, b); + assert(res < 0); // fails if: a before b does not return < 0 + } + { + char a[] = "test31"; + char b[] = "test22"; + int res = task2_compare(a, b); + assert(res > 0); // fails if: b before a does not return > 0\n"); + } + { + char a[] = "test1"; + char b[] = "test12"; + int res = task2_compare(a, b); + assert(res < 0); // fails if: strings equal up to end of a does not return < 0\n"); + } + + // testing 'task2_compare_n' + + { + char a[] = "test1"; + char b[] = "test12"; + int res = task2_compare_n(a, b, 0); + assert(res == 0); // fails if: count == 0 does not return 0 + } + + { + char a[] = "test12"; + char b[] = "test22"; + int res = task2_compare_n(a, b, 4); + assert(res == 0); // fails if: string equal up to count does not return 0 + } + { + char a[] = "test12"; + char b[] = "test22"; + int res = task2_compare_n(a, b, 5); + assert(res < 0); // fails if: a before b does not return < 0 + } + { + char a[] = "test22"; + char b[] = "test12"; + int res = task2_compare_n(a, b, 5); + assert(res > 0); // fails if: b before a does not return > 0 + } + + { + char a[] = "test1\0XX"; + char b[] = "test1\0YY"; + int res = task2_compare_n(a, b, 8); + assert(res == 0); // fails if: characters after termination are considered in the comparison + } + + // testing 'task2_copy' + + { + + const char src[] = "012345"; + char dest[20] = "XXXXXXXXXXXXXXXXXXX"; + + char* res = task2_copy(dest, src); + + assert(res == dest); // fails if: dest not returned from function + assert(dest[0] == '0'); // fails if: first char not present in dest + assert(dest[5] == '5'); // fails if: last char not present in dest + assert(dest[6] == '\0'); // fails if: dest not terminated correctly after copy + assert(dest[7] == 'X'); // fails if: manipulation of dest out of save range + } + + // testing 'task2_copy_n' + + char src[] = "0123456789"; + src[6] = '\0'; + + { // copying a range which terminates before count is reached + char dest[20] = "XXXXXXXXXXXXXXXXXXX"; + int n = 10; + char* res = task2_copy_n(dest, src, n); + assert(res == dest); // fails if: dest not returned from function + int i = 0; + for (; i != 6; ++i) + assert(dest[i] == src[i]); // fails if: expected range not copied + assert(dest[i] == '\0'); // fails if: null termination not present + i = i + 1; + for (; i != 19; ++i) + assert(dest[i] == 'X'); // fails if: manipulation of dest out of save range + } + + { // copying where count is reached before source range terminates + char dest[20] = "XXXXXXXXXXXXXXXXXXX"; + int n = 3; + char* res = task2_copy_n(dest, src, n); + assert(res == dest); // fails if: dest not returned from function + int i = 0; + for (; i != 2; ++i) + assert(dest[i] == src[i]); // fails if: expected range not copied + assert(dest[i] == src[i]); // fails if: null terminated + i = i + 1; + for (; i != 19; ++i) + assert(dest[i] == 'X'); // fails if: manipulation of dest out of save range + } + + printf("task2.test.c: all asserts passed\n"); + + return 0; +} diff --git a/exercise9/task3.c b/exercise9/task3.c new file mode 100644 index 0000000..b43794f --- /dev/null +++ b/exercise9/task3.c @@ -0,0 +1,12 @@ +/// @file +/// @brief Task3: function definitons + +#include "task3.h" // struct StringStats, task3_stringstats + +/// @todo Include C standard library headers as needed + +/// @todo Implement this function according the the requirements specified in task3.h +struct StringStats task3_stringstats(const char* str) { + struct StringStats res = {0, 0}; + return res; +} diff --git a/exercise9/task3.h b/exercise9/task3.h new file mode 100644 index 0000000..0e7b0d2 --- /dev/null +++ b/exercise9/task3.h @@ -0,0 +1,25 @@ +/// @file +/// @brief Task3: structure and function declarations + +#pragma once + +/// @brief todo +struct StringStats { + unsigned int lines; + unsigned int words; +}; + +/// @brief Counts the number of lines and words in a null-terminated multiline string 'str': +/// Line: +/// - a line always ends with a newline ('\n') character +/// - 'str' is always terminated by a empty last line (terminated with the null character) +/// - this empty last line is not reflected in the number of lines (e.g. an empty string has zero lines) +/// Word: +/// - a word is here defined as each continuous sequence of non-whitespace characters +/// (e.g. also punctuation can be part of a word) +/// - whitespace characters are identified using 'isspace' from the 'ctype.h' standard library header +/// @param str Pointer to a null-terminated character range representing a multiline string: +/// - 'str' is always terminated by a empty last line +/// - 'str' is always null-terminated +/// @return Structure holding the number of lines and words counted in 'str' +struct StringStats task3_stringstats(const char* str); diff --git a/exercise9/task3.test.c b/exercise9/task3.test.c new file mode 100644 index 0000000..10ed80b --- /dev/null +++ b/exercise9/task3.test.c @@ -0,0 +1,62 @@ +/// @file +/// @brief Task3: tests + +#include "task3.h" // struct StringStats, task3_stringstats + +#include // assert +#include // printf + +int main() { + + { // testing 'task3_stringstats' (empty string) + const char str[] = ""; + struct StringStats res = task3_stringstats(str); + assert(res.lines == 0); + assert(res.words == 0); + } + + { // testing 'task3_stringstats' (one line) + const char str[] = "this is one line\n"; + struct StringStats res = task3_stringstats(str); + printf("stats: %u %u\n", res.lines, res.words); + assert(res.lines == 1); + assert(res.words == 4); + } + + { // testing 'task3_stringstats' (two lines) + const char str[] = "this is \ntwo lines.\n"; + struct StringStats res = task3_stringstats(str); + printf("stats: %u %u\n", res.lines, res.words); + assert(res.lines == 2); + assert(res.words == 4); + } + + { // testing 'task3_stringstats' (two line leading spaces) + const char str[] = " this is \n two lines.\n"; + struct StringStats res = task3_stringstats(str); + printf("stats: %u %u\n", res.lines, res.words); + assert(res.lines == 2); + assert(res.words == 4); + } + + { // testing 'task3_stringstats' (multiple lines) + const char str[] = "this is \nmore than\ntwo lines.\n"; + struct StringStats res = task3_stringstats(str); + printf("stats: %u %u\n", res.lines, res.words); + assert(res.lines == 3); + assert(res.words == 6); + } + + { // testing 'task3_stringstats' (multiple lines, empty lines in between, leading spaces, trailing spaces) + const char str[] = " this is \n more than\n two lines \n including \n\n empty lines \n and spaces.\n"; + // const char str[] = " \n\n "; + struct StringStats res = task3_stringstats(str); + printf("stats: %u %u\n", res.lines, res.words); + assert(res.lines == 7); + assert(res.words == 11); + } + + printf("task3.test.c: all asserts passed\n"); + + return 0; +} diff --git a/lab0/.gitattributes b/lab0/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab0/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab0/.gitignore b/lab0/.gitignore new file mode 100644 index 0000000..a01b1e5 --- /dev/null +++ b/lab0/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab0/README.md b/lab0/README.md new file mode 100644 index 0000000..7284a39 --- /dev/null +++ b/lab0/README.md @@ -0,0 +1,138 @@ +# Labor Setup (Praxisteil, 150min) + +- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**. +- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint. +- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben. +- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben. +- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren. + +--- + +Im heutigen Labor sollen Sie die folgenden drei Aufgaben abarbeiten. + +--- + +### A. Vervollständigen der Konfiguration im Labor + +Sie sollen die Konfiguration der Entwicklungsumgebung auf Ihrem Laborrechner mit den unten stehenden Befehlen vervollständigen (nur Schritt 2. und 5. nehmen eine Konfigurationsänderung vor, alle anderen Schritte sind rein informativ, sollen aber trotzdem ausgeführt werden). + +1. **Betriebssystem**, bereits installiert. + ```shell + uname -a + ``` +2. **git**, bereits installiert, **aber noch nicht konfiguriert** (Email und Namen in den Befehlen bitte anpassen) + + ```shell + git version + + # TODO: adapt the student ID and name in the commands below + + git config --global user.email "eXXXXXXX@student.tuwien.ac.at" + git config --global user.name "Firstname Lastname" + git config --global init.defaultBranch main + git config --global credential.helper "cache --timeout 300" + ``` + +3. **Compiler/Tools**, bereits installiert. + + ```shell + gcc --version + g++ --version + gdb --version + clangd --version + cmake --version + ``` + +4. **VSCode**, bereits installiert. + + ```shell + code --version + ``` + +5. **VSCode Erweiterungen**, **noch nicht installiert**. + + ```shell + code --install-extension ms-python.python + code --install-extension ms-toolsai.jupyter + code --install-extension ms-vscode.cpptools + code --install-extension llvm-vs-code-extensions.vscode-clangd + ``` + +**Demonstration:** Führen Sie die folgenden Befehle aus: + +```shell +git config --get user.name # should print your name +git config --get user.email # should print your student-email +code --list-extensions # should list all installed vscode extensions +``` + +--- + +### B. Kennenlernen der Systemkonfiguration im Labor und Einüben der Labor-Abgabe via TUWEL + +1. TUWEL im Web-Browser aufrufen und einloggen +2. Anweisungen der Aufgabe unter [Labor Setup: Abgabe (demo)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse3) folgen + +**Demonstration:** Die von Ihnen hochgeladene Datei `task.cpp` in TUWEL. + +--- + +### C. Hausübung 1 "Schnelldurchgang" + +Schritt-für-Schritt Anleitung des Abgabeprozesses für `exercise1` mit *git*: https://sgit.iue.tuwien.ac.at/360050/git + +Sie sollen **Aufgabe 2** (Task2) der ersten Hausübung `exercise1` bearbeiten und dabei alle nötigen Befehle um die Hausübungen abzugeben kennenlernen: + +- Kopieren der Angabe in Ihren persönlichen Bereich auf dem git-Server: + + - Navigieren Sie zu [https://sgit.iue.tuwien.ac.at/360050/exercise1](https://sgit.iue.tuwien.ac.at/360050/exercise1) + - Klicken Sie oben rechts auf **Fork** und dann auf **Fork Repository**. Das Repo *exercise1* sollte jetzt in Ihrem persönlichem Bereich verfügbar sein (s. oben rechts *'Profile'*). + +- Herunterladen der Hausübung 1 vom Git-Server und öffnen in VSCode: + ```shell + mkdir 360050 # just a recommendation for a directory structure + cd 360050 + + # NOTE: next command prompts for username/password + # NOTE: replace eXXXXXXX with your student ID + # NOTE: --recursive is important, otherwise the 'modules' folder will be empty + + git clone --recursive https://sgit.iue.tuwien.ac.at/eXXXXXXX/exercise1.git + cd exercise1 + code . + + #TODO: check if all files are present, especially inside the 'modules' folder + ``` + +--- +--- + +- Bearbeiten Sie **Aufgabe 2** der Hausübung 1 **(task2.main.cpp)**: + - Die genaue Beschreibung und Anforderungen finden Sie in **main.ipynb** + +--- +--- + +- Wenn Sie fertig sind nutzen Sie folgende Befehle um die lokalen Änderungen an der Hausübung (Ihre Lösung) auf den git-Server hochzuladen: + + ```shell + git status # some files should be marked as modified or new (red) + git add . # add all files to staging area (green) + git status # check staging (green) + git commit -m "task2 done" # apply all changes + + # NOTE: next command prompts for username/password + git push + ``` + +- Wenn Sie nachträglich etwas an Ihrer Lösung geändert haben und diese Änderungen auf den Git-Server hochladen wollen, führen Sie folgende Befehle aus: + + ```shell + git status # modified files are marked (red) + git add . # add all files to staging area (green) + git status # check staging (green) + git commit -m "changes done" # apply all changes + git push + ``` + +**Demonstration:** Öffnen Sie Ihr Abgabe-Repo auf http://sgit.iue.tuwien.ac.at im Browser und offnen Sie Ihre Loesung in **task2.main.cpp** im Browser. diff --git a/lab1/.clang-format b/lab1/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/lab1/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/lab1/.clangd b/lab1/.clangd new file mode 100644 index 0000000..0310bd3 --- /dev/null +++ b/lab1/.clangd @@ -0,0 +1,23 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: No + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/lab1/.gitattributes b/lab1/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab1/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab1/.gitignore b/lab1/.gitignore new file mode 100644 index 0000000..a01b1e5 --- /dev/null +++ b/lab1/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab1/.gitmodules b/lab1/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/lab1/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/lab1/CMakeLists.txt b/lab1/CMakeLists.txt new file mode 100644 index 0000000..583a48c --- /dev/null +++ b/lab1/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(lab1 LANGUAGES CXX + DESCRIPTION "lab1" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/lab1") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # avoid ctest dashboard targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_TESTING "enable testing with ctest" ON) + +# testing + + include(CTest) + +# global includes + + include_directories(modules) + +# include own targets + + add_executable(taskA taskA.cpp) + add_test(NAME taskA COMMAND taskA WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(taskB taskB.cpp) + add_test(NAME taskB COMMAND taskB WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + + add_executable(taskC taskC.cpp) + add_test(NAME taskC COMMAND taskC WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + diff --git a/lab1/README.md b/lab1/README.md new file mode 100644 index 0000000..8279536 --- /dev/null +++ b/lab1/README.md @@ -0,0 +1,25 @@ +# Labor I: Abgabe (30min) + +**Dieser Teil wird über [TUWEL, Labor I: Abgabe (30min)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse5)** abgewickelt. + +# Labor I: Praxisteil (120min) + +- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**. +- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint. +- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben. +- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben. +- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren. + +--- + +Im heutigen Labor sollen Sie die folgende drei Aufgabengebiete bearbeiten. + +### A. Namensräume zur Abgrenzung von Symbolen + +### B. Zweidimensionale Felder lesen und schreiben (`std::vector`) + +### C. Einbindung von lokalen Biblotheken + +--- + +Details zu den Aufgaben finden Sie in [`main.ipynb`](main.ipynb). \ No newline at end of file diff --git a/lab1/compile_flags.txt b/lab1/compile_flags.txt new file mode 100644 index 0000000..bde723f --- /dev/null +++ b/lab1/compile_flags.txt @@ -0,0 +1 @@ +-Imodules \ No newline at end of file diff --git a/lab1/main.ipynb b/lab1/main.ipynb new file mode 100644 index 0000000..ff0d5a7 --- /dev/null +++ b/lab1/main.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A. Namensräume\n", + "\n", + "Namensräume (`namespace` s) sind eine Möglichkeit, um Quellcode besser zu organisieren und strukturieren: Namenskonflikte zwischen gleichnamigen Funktionen (wie es beim importieren mehrerer Bibliotheken auftreten kann) können vermieden werden.\n", + "\n", + "**Aufgabe 1:** Setzen Sie die folgenden Schritte in der Datei `taskA.cpp` um:\n", + "\n", + "1. Definieren Sie in einem Namensraum eine Funktion, die einen beliebigen Text (z.B. 'Hello from namespace XXX') in der Konsole ausgibt.\n", + "2. Definieren Sie in einem anderen Namensraum eine zweite Funktion mit gleichem Namen, die einen anderen Text (z.B. 'Hello from namespace YYY') in der Konsole ausgibt\n", + "3. Schreiben Sie eine `main`-Funktion, in der Sie beide Funktionen aufrufen.\n", + "\n", + "**Aufgabe 2:**\n", + "\n", + "Diskutieren Sie Vor- und Nachteile der folgenden drei Header-Dateien: Typen und Werte werden aus der Standardbibliothek inkludiert und für die Schnittstellen (Funktionsdeklarationen) verwendet:\n", + "\n", + "```cpp\n", + "// Option1.hpp \n", + "#include \n", + "#include \n", + "\n", + "using namespace std;\n", + "using namespace std::numbers;\n", + "\n", + "vector doSomething(vector data, double ref = pi);\n", + "```\n", + "\n", + "---\n", + "\n", + "```cpp\n", + "// Option2.hpp\n", + "#include \n", + "#include \n", + "\n", + "std::vector doSomething(std::vector data, double ref = std::numbers::pi);\n", + "```\n", + "\n", + "---\n", + "\n", + "```cpp\n", + "// Option3.hpp\t\n", + "#include \n", + "#include \n", + "\n", + "constexpr double Pi = std::numbers::pi;\n", + "using Vector = std::vector;\n", + "\n", + "Vector doSomething(Vector data, double ref = Pi);\t\n", + "```\n", + "\n", + "**Demonstration:** Führen Sie Ihr Programm aus Teil 1 aus und fassen Sie Ihre Diskussion zu Teil 2 kurz zusammen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## B. std::vector und Funktionen mit Schleifen und Bedingungen\n", + "\n", + "In der Datei `taskB.cpp` sind ein Vektor `vec` und eine Matrix (*vector of vectors*) `mat` gegeben:\n", + "\n", + "```cpp\n", + "using Vector = std::vector;\n", + "using Matrix = std::vector>;\n", + "```\n", + "\n", + "**Aufgabe:** Implementieren Sie die folgenden Funktionen:\n", + "\n", + " 1. `sum`: **Summe** aller Elemente eines `Vector`\n", + " 2. `sum`: **Summe** aller Elemente einer `Matrix`\n", + " 3. `print`: **Formatierte Ausgabe** (in der Konsole) eines `Vectors` (in einer Zeile) \n", + " 3. `print`: **Formatierte Ausgabe** (in der Konsole) einer `Matrix` (Zeile für Zeile) \n", + " 4. `cout_even`: **Abzählen** wie viele Elemente einer `Matrix` gerade sind\n", + " 5. `mean`: **Durchschnitt aller Elemente** einer `Matrix`\n", + "\n", + "**Überlegen Sie sich,**\n", + "- welche Typen die Parameter Ihrer Funktionen benötigen, und\n", + "- ob Ihre Funktionen einen Rückgabewert liefern, und welchen Typ dieser haben soll.\n", + "\n", + "Rufen Sie Ihre Funktionen in der `main`-Funktion auf, und geben Sie jeweils deren Rückgabewert in der Konsole aus.\n", + "\n", + "**Demonstration:** Führen Sie Ihr Programm aus und interpretieren Sie die Ausgaben in der Konsole." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## C. Nutzung der in `modules` bereitgestellten Bibliotheken\n", + "\n", + "Wir stellen Ihnen in diesem Semester im Repo [`modules`](https://sgit.iue.tuwien.ac.at/360050/modules) Funktionalität zur Verfügung, von der Sie in einigen der folgenden Hausübungen Gebrauch machen sollen. \n", + "\n", + "**Aufgabe:** Setzen Sie die folgenden Schritte in der Datei `taskC.cpp` um:\n", + "\n", + "- Schreiben Sie eine Funktion, die eine $m \\times n$-Matrix (*vector of vectors*) zurückgibt. Die Anzahl der Zeilen und Spalten soll als Funktionsparameter übergeben werden. Die Matrix soll mit aufsteigenden Werten vom Typ `double` gefüllt werden, wobei mit dem Wert `1.0` gestartet und dann reihenweise vorgegangen wird.\n", + "\n", + "- Schreiben Sie eine `main`-Funktion, rufen Sie von dort Ihre Funktion auf und speichern Sie deren Rückgabewert (Matrix) in einer lokalen Variable.\n", + "\n", + "- Rufen Sie die Funktion `iue::io::savetxt()`, die in [`modules/iue-io/csv.hpp`](modules/iue-io/csv.hpp) definiert ist, auf, um die Matrix in eine `.csv`-Datei zu schreiben.\n", + "\n", + "**Hinweise:**\n", + "\n", + "- Ein Beispiel für eine Matrix mit 4 Zeilen und 2 Spalten wäre:\n", + "\n", + " ```\n", + " 1 2\n", + " 3 4\n", + " 5 6\n", + " 7 8\n", + " ```\n", + "\n", + "- Die Funktion `iue::io::savetxt(...)` befindet sich im *namespace* `iue::io` in der Datei [`modules/iue-io/csv.hpp`](modules/iue-io/csv.hpp).\n", + "- Sie müssen die Header-Datei am Anfang Ihres Programms inkludieren (relativer Pfad), und wir empfehlen dazu folgende Vorgehensweise:\n", + "\n", + " ```cpp\n", + " #include \"iue-io/csv.hpp\"\n", + " ```\n", + "\n", + " und zudem den `modules`-Ordner als Include-Pfad (`-I`) beim Kompilieren angeben:\n", + "\n", + " ```shell\n", + " g++ -std=c++20 -Imodules ...\n", + " ```\n", + "\n", + "**Demonstration:** \n", + " \n", + "- Rufen Sie Ihr Programm auf und inspizieren Sie die resultierende `.csv`-Datei. \n", + "- Ändern Sie die Anzahl an Zeilen oder Spalten, kompilieren Sie Ihr Programm erneut und erzeugen Sie eine weitere `.csv`-Datei." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab1/task.cpp b/lab1/task.cpp new file mode 100644 index 0000000..803c06e --- /dev/null +++ b/lab1/task.cpp @@ -0,0 +1,14 @@ +// +// Created by e12302343 on 3/20/24. +// + +#include + + +int mul(int a, int b, int c) { + return a*b*c; +} + +int main() { + std::cout << mul(1,2,3) << std::endl; +} \ No newline at end of file diff --git a/lab1/taskA.cpp b/lab1/taskA.cpp new file mode 100644 index 0000000..3e57e2f --- /dev/null +++ b/lab1/taskA.cpp @@ -0,0 +1,26 @@ +/// @file +/// @brief task A +/// g++ -std=c++20 taskA.cpp -o taskA && ./taskA + +#include + +/// @todo create a namespace with one function in it +namespace taskA { + void print() { + std::cout << "Hello from taskA" << std::endl; + } +} + +/// @todo create a second namespace with one function in it, use the same function name as above +namespace taskB { + void print() { + std::cout << "Hello from taskB" << std::endl; + } +} + +/// @todo write a main-function and call the two functions you defined above +int main() { + taskA::print(); + taskB::print(); + return 0; +} diff --git a/lab1/taskB.cpp b/lab1/taskB.cpp new file mode 100644 index 0000000..1c0ea88 --- /dev/null +++ b/lab1/taskB.cpp @@ -0,0 +1,95 @@ +/// @file +/// @brief task B +/// g++ -std=c++20 taskB.cpp -o taskB && ./taskB + +#include +#include + +/// @todo implement your functions here, details see main.ipynb +/// @todo " 1. `sum`: **Summe** aller Elemente eines `Vector'", +int sum(std::vector vector) { + double sum = 0; + for (auto i : vector) { + sum += i; + } + return sum; +} + +/// @todo " 2. `sum`: **Summe** aller Elemente einer `Matrix`", +int sum(std::vector> matrix) { + int sum = 0; + for (auto row : matrix) { + for (auto i : row) { + sum += i; + } + } + return sum; +} + +/// @todo " 3. `print`: **Formatierte Ausgabe** (in der Konsole) eines `Vectors` (in einer Zeile)", +void printVector(std::vector vector) { + for (auto i : vector) { + std::cout << i << " "; + } + std::cout << std::endl; +} + +/// @todo " 3. `print`: **Formatierte Ausgabe** (in der Konsole) einer `Matrix` (Zeile für Zeile)", +void printMatrix(std::vector> matrix) { + for (auto row : matrix) { + for (auto i : row) { + std::cout << i << " "; + } + std::cout << std::endl; + } +} + +/// @todo " 4. `cout_even`: **Abzählen** wie viele Elemente einer `Matrix` gerade sind", +int countEven(std::vector> matrix) { + int count = 0; + for (auto row : matrix) { + for (auto i : row) { + if (i % 2 == 0) { + count++; + } + } + } + return count; +} + +/// @todo " 5. `mean`: **Durchschnitt aller Elemente** einer `Matrix`", +float meanMatrix(std::vector> matrix) { + int sum = 0; + int count = 0; + for (auto row : matrix) { + for (auto i : row) { + sum += i; + count++; + } + } + return (float)sum / count; +} + +int main() { + + // one-dimensional sequence (vector) with 5 values + std::vector vec{1.2, 2.5, 3., 4.1, 5.9}; + + // two-dimensional sequence, here we call it a matrix, with 3 rows with 4 values in each row + std::vector> mat{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; + + /// @todo call your functions using 'vec' or 'mat' as arguments and print the results + + std::cout << "Sum of vector: " << sum(vec) << std::endl; + std::cout << "Sum of matrix: " << sum(mat) << std::endl; + std::cout << "Print vector: "; + printVector(vec); + std::cout << "Print matrix: "; + printMatrix(mat); + std::cout << "Count even: " << countEven(mat) << std::endl; + std::cout << "Mean of matrix: " << meanMatrix(mat) << std::endl; + + + + return 0; +} \ No newline at end of file diff --git a/lab1/taskC.cpp b/lab1/taskC.cpp new file mode 100644 index 0000000..3e5fa55 --- /dev/null +++ b/lab1/taskC.cpp @@ -0,0 +1,43 @@ +/// @file +/// @brief task C +/// g++ -std=c++20 -Imodules taskC.cpp -o taskC && ./taskC + +/// @todo include modules library header for using iue::io::savetxt +#include +#include +#include + +/// @todo write a function initalizing a 'vector of vectors' with an integer sequence, details see main.ipynb +std::vector> initMatrix(int m, int n) { + std::vector> matrix; + double value = 1.0; + for (int i = 0; i < m; i++) { + std::vector row; + for (int j = 0; j < n; j++) { + row.push_back(value); + value++; + } + matrix.push_back(row); + } + return matrix; +} + +/// @todo write a function to write a 'vector of vectors' to a csv-file, details see main.ipynb +void writeMatrix(std::vector> matrix, std::string filename) { + std::ofstream file(filename); + for (auto row : matrix) { + for (auto i : row) { + file << i << ","; + } + file << std::endl; + } + file.close(); +} + +/// @todo write a main-function and use your function to +/// - init a 'vector of vectors' to initialize a table and +/// - write the table to a csv-file using iue::io::savetxt +int main() { + std::vector> matrix = initMatrix(3, 4); + writeMatrix(matrix, "matrix.csv"); +} \ No newline at end of file diff --git a/lab2/.clang-format b/lab2/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/lab2/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/lab2/.clangd b/lab2/.clangd new file mode 100644 index 0000000..0310bd3 --- /dev/null +++ b/lab2/.clangd @@ -0,0 +1,23 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: No + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/lab2/.gitattributes b/lab2/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab2/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab2/.gitignore b/lab2/.gitignore new file mode 100644 index 0000000..a01b1e5 --- /dev/null +++ b/lab2/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab2/.gitmodules b/lab2/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/lab2/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/lab2/CMakeLists.txt b/lab2/CMakeLists.txt new file mode 100644 index 0000000..5686057 --- /dev/null +++ b/lab2/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(lab2 LANGUAGES CXX + DESCRIPTION "lab2" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/lab2") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# include own targets + + add_executable(taskA taskA.cpp) + add_executable(taskB taskB.cpp) + add_executable(taskC taskC.cpp) diff --git a/lab2/README.md b/lab2/README.md new file mode 100644 index 0000000..9b1303c --- /dev/null +++ b/lab2/README.md @@ -0,0 +1,25 @@ +# Labor II: Abgabe (30min) + +**Dieser Teil wird über [TUWEL, Labor II: Abgabe (30min)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse7)** abgewickelt. + +# Labor II: Praxisteil (120min) + +- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**. +- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint. +- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben. +- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben. +- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren. + +--- + +Im heutigen Labor sollen Sie die folgende drei Aufgabengebiete bearbeiten. + +### A. Debugging + +### B. Klassen + +### C. Überladen von Operatoren + +--- + +Details zu den Aufgaben finden Sie in [`main.ipynb`](main.ipynb). \ No newline at end of file diff --git a/lab2/compile_flags.txt b/lab2/compile_flags.txt new file mode 100644 index 0000000..bde723f --- /dev/null +++ b/lab2/compile_flags.txt @@ -0,0 +1 @@ +-Imodules \ No newline at end of file diff --git a/lab2/images/01.jpg b/lab2/images/01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a28493602fb9cde804ed67f87e58a8f5f8afa31 GIT binary patch literal 53838 zcmeFa30%_ozCTP;O*5_2Voq9lCd(Sv$+gntRHJ5&Sr(|EavF0bWZZW-Gfi5WdexMp zOgd(o3P>)10>VtDrby+2BnYGi<^s5+fGj@0&N=s-Irq8udENiL|NA`m{-4ZmS-|)A zUEbf%_w#vwmeyXay#{@D!QImxv~JxxP%`ifv?c(Z1Fiq)qxavy*9PEw<0l(8ZrHGK z^QKK7f3kJ+)~#DMZ`rbK`>xNnZQr?l%a+gfeztS>9s>h|tvd`q-@E7YU3(1nyzgY) zdf+n~Hh#Ks-V6^WC z{MWB_9|2?hc+)4JZr%dCf&3Zhqjl@of3#u!`%we$&H!EqZP>YS*B8gne7yVewM~a& z_Sk-3SoMkN*`{9&d60~8h*ad=!n_TV_%xvJ2*O>IC<*a*KY0}=RGf6 zxe9^$`3Jy4u7`$&M?^-&#U~^tC8wm`{P9*+_U$`4MaB0@O0j<`D}PY^u%`A=U427y zODmZ|ZEL4J>3rJN-Se!sZ*XXsJHmT4${(MYd?S%gy`7#}SX_FyyrNX8SKrTT9ccYu zruDa({fl|+1m^Y8h7IdCY&FpUz`}@53psnlI0h_meCkPDE&OFFD z2Ks-eWpogD)SUuB&tc_m+n)U~jnXhQt)UgvXa2mVRusX%)s?3stT>G<+Q-2Iy9auW zZA4J0h6t(q%lK-Fo=;dL%Mb3`D{>FbcSn?zWv-0Wy?=3ZewA=p7>t4z9cb#Is{*L$ zVwDVZrQqCf4dnWed=#!K-TMghhndQE+h}W`UlNI5niWCE=K95=u!lcyRm>f@qW_+X zP;DRn?R$)lt#{UJy1EAX8O77DC!4%F^+>lqnl*m_@16sCuK$$_eruet2KuASV`Y2& z8tC@|l6F(nz=J>9u^uP*tbQ!Symr2 z*fq-@mpM+CI~(M18ylm`GM6mVuo8sA&dOuL2P7C(Wu%`O*Ii3LS4GH0#~dByi;w}v zazddT)gXS-*WbEsiuE_3)%^x!F#Ag%C;bL!;J<+knzv`&{oeqs;BRo|R=a`1e*@|2 z-@rLy;P?B)AD-m=|G(7>^Q@Y=Y+*O8jKXWZw?NF_hLk)Ay&eWdOu$GJ8v{bf3_kv_ zpt=~nU@KFz7G^-*|B2gSeT^y{xor*fv|uw|c8^ zoDj+4PT7RlE#8AcIoZ=*<1jDIe#3pJ#*BE|5()`lk@{FEWlS_y(zpoz(Xe8_3O<3( zK9%>*6Oi>=)_V=XRzDdnfkq?}aB%eeGa`ji;H_$UAXaS$f2tyNCZ%@dRaBW(JrYi* z7QGr1s4Hhp!y>{0OwlFG86AJxb(i)E85QC&pOKx-^JyAp#MI8bY!=y9hUT9%fSQyg zc`BJJ3e`3cgy*_0BF1}?=yYbGwVZd`#bhhiue5j?98PJ=+AFX-=k!xQ$AsMub65j~ z;uL;&{g_%`Ra2D;(QRVTx(3>od@;x_kY{7vN|*UgUDbSz-0_nrX>@#%TfI~U!NnJ$ zH2C0+zu&m$@_${+I5+R{3k`i!zlF14cX7e&X7~g&d3TPjI@U>13T6yY6by1D@i*wBMKP@RHAZq~h;)Xxy(l!m*5z8Ug4T+BJzKX_hH zR=0ix94=NZsjAjM-v$G}Rh@&CV}H>(^2RgCM6X9Ps|U$7)0EU~Y<*N|tOKv?a7-_) z3leR`i^o+*?xUKofylw(uf+$V4t`P)4F3dGqklCh9gKEa4ijv>OX#NuWTHzj9YJ9o z3rgt1$Hfy{0^S|?*h29ctP|M~_41q(U(P7aD`7^KpAaaKbT$MkRA$O~;54}oq0Tqf z2oTx=e^tk8@u%9aRl>UDq(l;OA~$cZ@$CegQ{W~K`%|gU9=Ez8{?s?nqi^XCB8X@7 z2wdJgB8PD%e7+};xll@U(thCtbKzgitqP>d6^+=ao+BMBp{_rt*)xW!!0fj315fPW zTln)xHV!r_k96=O)eo~idbVEBlr zpOF9ms%23}CaE{PS_APSS?@ldf#n9O<2z1c?mR4Gy#~_}x{|u}yK;{G{Lz27VG~%| zC9Ga=vj*DjK8sOL+32(gz1bfRJ)p$XPn%8bEm}dc(YPq}ae{!QsD-ubu;VPXo{O2^ z4%VLxx>RLeD9vNOi+#?BWS9DB$~c#UY*r9iYoI9&U=I9pVhyB{n2cr`uYoFMSJptF z$u-dAJw5&kTY-?;5U(t0D%JbB27`cLR-yG6vX6Fs!5S$4@?W0<{_|5jxKSw?v)azt zt6Ke(-owEyp7k$##QfvOmthr%)HTpot6VU+eGOFj?;2GG^buCX>USmkypUtw#9j6C z`o~fH-=+nrmnca~JN`$_!e2QMz!!S%_i||>3By96fapzZx&P%A70aG;4eH=(()*yq z!20h%$O90*^c4uQ`<<74O=H-T~SLs9m7C*6+3m8?o6MPNy;v_~fu?AYOUuW=dJreZciT~YJ z6>>UHTaN0vzgJ8rmoR5-lrY@Ci3uaIpwvp)s)n$p@Am+xDX_S#YY$lix%!!?_OP_k z%}Kw7LG(YsXFXgK+oMl!1kZnoQ3*{1rQjt=3?OK|)g=llN5pWuu2i2G)Og&z3|;m%TXJvnN2B=su)Ht`THY*&0&F3S%rJd@I<4pO zY*9oaI6BDC8wVW*XY9}PCZwhJx`qV_l5G0uI9>Uag+ul$ix zd3~Nk;#(hTFD&({U3^pAONBs}4lj-uQnW1dm+x4mYoOoh;mc%Z+QCSG30n8Jmg?Fx z5B6_Tc|1&aABvvSo*UQH8}Tlo9LgRJ$eq$33oXoLWAp&HoTBJZCOJ(w{h|vQ_xq7y z*N~jQ`n<=VSJG%YFR;sCig@~fv2W;!-OcF&2rfwjPK&gOH&q-=fRJ~nR?$ko_Z!mk z+rNN_e;dVbNA%(6zwge!e=Rm@5UM4_>Ra*Z=Ar3Q)`FC^JivNq@aW~ofM+}X@*3z9 z^j-T7Bs(&MjEm1wG?tCTE!UnW<0ENqER=>_T^G;)B7P%LRf0o$ldZA~@zK`fAWI6Q1u_f7ROzSi2gXYw(=J?4A}K?ie;(*Z^JXiLCPPJzR7y z293A;r)NB@WuK-rEDswGgd#LrjBm|6&QLjDdoX=jg&R*S3qMG%gD1c6$_twp5sg#W zhf|xi`2D$W2*-O(J*lyJg`Avz(krzHP&<9BE30&wXIu61hTZl1LD=shgF9m~Tr5+m-Q3gN4@lejF-z>kR}~dz0rMmy;6z%sPXc>jM3fux*v7y z1AMKQE?sup1NwV&b%?y~KM?RRjNicO`u)}igc&2$NO6Y4eHnq0^iP!0-&Hu~pBHEd z?_;1I(uSg~&6Z3HQkzK&HcBL|NfokUaRTk)M>sR?oOVlyKG1LFFea9zSS(p$WRBvM zBq%t?Wm7{k^hESgf08}DK&9kbK3pQ!0{pm-0@Eyj>&mAq4t`f)U#-i`Vm=RK^w{Zg zQvik=vmmL~>|N3)1DY?}cObC)Ys4#Ri^7J1_zj_|E77hE;xIB`}1zVrCghPU={C9JY0y+7%7C5AzzrM9XLDb z&B&S=iToYyR_XaSd6ed6-o=B2NXT8uOX*YJ5}5CgsTIOWg#iLFmOR)K$aBawvAL9Q zGN(a0MC^SLhe9vsDzRu~5@7Ntu>o)OPD}d(HeI{ns@Q3gJvSd*Mv*}UOXd`*Q17Qg z{>)>#syd$ro8UYcG>XP$Kpp3GfJP{?(I9krDO}`jqEW?M!qSQP_ufKt)48SClIV!{MQl=r9*Qz_`jlD9sjCUZnRi zJfmL^Fer7;chj(Se3`vT!QN9rysYIdFSd<|qhJTi`9Y1sE9_SPm1cE?Pm!3x7u!Yi z#ll9zc@1l1s)5xZ*0Nszr0eMn0(;I|V<)y<@n?Kb_DZR3(KuqKU@5uu8@7QdxfJ&Z zfdqnLbp`RdY(DD2i~|9Gs$#!kaI<|HX3O--%G_hZP;ek>4dk~!4J*e-hY0~@kR0ff zoq04;zia+^O-RjKY9S&DhKy9^nbp(!CKDB62Nssz4+#@!ICvd@aM<<4xruGtUz{0v zw|CgyILKPm?HtNje3La{AXurpz9?XaAsF-k*qwCi<9g~R@6fBSL@M-a-FC#rS8r2m z@(9e|El~O3mguQ9P{6m3NvqD7l=R*btVF{O_IN+wnS*8vZuvKnMvYYo-%%P`C|szJ zDg_ii`ZnYwDdz~+^3A-SSg%5QHc_e6mO&Z$Q==0jpK~)&bf|Yf9S1}@7#Lx@@=h}# zPC51kTYG9%cgVN6ru3qP+I$Cr=2?121A%BJ6)Kr1a!^Y4%1Mt%@HQ8_2^+&mz44$+ zP+VL_Oug0JBF~5F?__GKw{Z%m9a54BT@(PLNNo_O+Yd_SCLN-bYaajG($1hGPmJ4JnZ!0d$wuNARfl^OH}Irp|+7#CNUQLT*WZ4bGXPG00CahB}! z7IibGL%7vfm1G;q(6W<5!T04sm3JL>eDW%}+1sOQ?}{vDTT5hS5Tm~dK+}Dxon=L^ zR6}5O(ilH5Z>oM>*yJ^j=-#nAI8tPS)$b~3(UqRQ;3a$MK6tia$kH`fVaGgd5)wy> z*&&RY%rtCZFPJ`=nIRAx;97uj3EGQSvAfH&^Oiq}w%KceOiLaSz3^#8So24K_|MW9 zl3M7$;-oYRTJDe#I2*Z)+{3Xz@&uv`M*C_h&wQcYI)4)Gzx=|ka}8vu4X8hdAARv) zc0l)F!rRLWVe4Es4h>uSB20aq&q{$uE5>W5^;Uxly@_h_+{<^6A^YST*V>k>uP-&q z55sRmt`{!fqk>cyTbR5anF|Vvjzt0sVz)H|^ zp6SGn1O98E>thj_`v!UE2c&%J?iU)ifJ1Cg?*-pVGN+c({d6lxFSeR&1cDmZK#SI5?-Xf@cws^6xgVD(Q)9UA;|Gt@lPfYQ z*ySND$7hT7fPQqrjQF!CD2l~5%22+#9<+x{-CnKo z>4xnupEwg9KC$Jr_2Qs2q%iuh@DL%_G@VUF3-wmR0#Y77lR(|lkenz@#!zc{#MiA{ z*B2&bW@2GK(<`Nqp1G(-QImuXr?R`zRB8@*!iTDf3(STA))#5n6>gV;Ns?;BX@KBQ zd_>l=0EBFaRRu8-1=^2#?znE7P7Uh0Yp-+a+7(6X$qGT{*1>%iuN65ILKy8X-D+&C z9177}5x9~Paqog3XThn!0X@f38(GP%v<0o09@2f=UV5S!7cFFU25vmU!C)t@Na&9N ztG0LZu?K(r(Eiugz%BxCLbOPG4en<8HY$!Yl^XnI-GkXdutrLRkyed>SV{La&`aoj z${)Y{>%SMY2Bmtefzr90R@Bd%F%h(P1$uS0%rLkRh*fi_LR-MbJZw=*rYLqh1glr0 zOYk&qqmA*nxNKs}9cwhHu@vPHeSOI+-#{q!)c2znNg7BXQ})Xe;;$VKtmKl;uYnZh z?-bj`Gls$Ycs68u7HZQRDjbbE$~F!}A*@0QQvhhLwgku|Uuh4vsCY4DVB|Edry&NtmhU~m_N5HM5x5c8?>B7;&)*NQ@1)Vww^-j!-lUJ zm6(32dlb^;I*>4JqFeIdTX&cOo)u&`Y#{7q!k2{T!{pM4%q4%}xdaoT5QwF=2X6Cq zQ8g6$7ssCOf7W+dl1_N^&-4CkCw`6t6oT8oHnrAuK7Ud(^ugFyKpPyc#-{(p0w ze4GGeh#AWM?((Pw)Xbjpe^y8;|C~Lxds>P31C!c@Om0js+v{^ELAbwij;e8JpYYj- zd2-pIt4mt&EwvcK?vD+OMkpb*p+FzVpC07@;Z(3+B z@6cLMLOnP3-sP`r`v2_M4(4c1i2_s?!A8kg$Z)I1d56k_yv^}MC^K8;XSmxPzduGG zUouWqbWqWKVkdU6o!fVlU#)@Q58n1ca~-R%yyE5s|LV5KFA55^O`QSqrjGxYCcphx zCf5Bo?R($*gfs*hFhc;7qcIQ2oF}@L{bOX!pYzqckl&3Y?d->;KNG~N3-LC(MX`ln z0wFkRm-gh6%ee$A$@20brfL`uI^X}yKEnpw_N>eNO&C@vNt;5_#iMl_Bj5em zf!aXFMd@Zb>LX6o-ff~(*9!d!l{5OG=mGf}C@BF#Z7Uy{IQetYd9ysK$6*w?gf-eY zG4lkq=~rZxFd7RRX{>6??@kG#oJp8$S?uK-jM3kW>BcrVk`%gmjW?_8t#zf3^p5{mbde12> z?KErERWU&;mH?)l8mC_$a54C~%hTt9O(*Y%c%h7;j3H6PqoNV_TMxf4s}4iR<@IF6 zj?zXM%cmi1JywMfz`+YZq89dvH?N=K_PTh)^I#dQ;zw#H zqtid(us+l!*9e}xS0V>aaY9|a@8x?TpWUl@(U(Gw981oe;B`_`hdd2Oqic&q4p3Cz zfJ{i&uadr*x5+k0Ky7m4fyYg9#J<^ak<9@u!L6|tsN(1T0yY|m`1hw{XylMzT}_&J zF6*mxRnXOmk;PSH(9y(^;lq#uWg2Ti#!A7LCpRKyhr4)s=2G`DlE?i0j>e|&3DQnP zV_`@}_?w<+sw4Pqsm#Smj0oJ%qhSY3laFw17^VA6b2m%JjuEm41BP;Ttm!adYBkZ{_n@o<*2%#4GA zCux_;9sE})T%i)Ko0+hPid!j^+TU$|u*Z+2GVIKuQEon%t(-6DLf4;bZymK9CBaS_ zPObXWb1h{* z=TGo_iXvW9{U^f;Avg&{umqLX(K72B)6Wj@Jce$}#naR3iO?;O0MnwAB}pljY0@#V zwT$(ITtJna?}8}(*b6R2Im_JA=bcyQw_`uPS?hJMJ{-9sE#h1+n(QwuVTNglKsGlL zlO`v|=`9Bc@vIG`(Q5z{gBfJ_eZ15@3QtbLBo;lLjrJ`GM()ons18oPc`3j9iHYls zTbRGnT*nPVcj)`bz9jSzu%SO8>2Ud?zH_?-{aCIJz{YCDPgto843uZ> zP-cp}E&Db?578X?37~kDyiz=^(IC_pA#A?(XcY`GyPkhMARU@)8~Q^8w>qE^D+yvHDKShnX8Qis8I5htmD0dR zC6|Ml6r&xtCg(XFT_ZoxGPrl0%709~b$2LmEJd7lBX>(mnx7Hx?CVHHX0LOpN!8iT6${qW1{0{ zYNfA*ZBM(O9B-GQSb;Q@r20Go?;P`5_{adB;d+FYKVAmyHepZ}GsIYB8cyXGwFYXT zN(kbQTQlt!5*Vo+M8ac%U@S1BeDJw5qSgMrv}}h6l~KZgFP-N~fPB@2{h`(&w;#}7 z$-h`Yf3Nk-NDFx+2#$<}_aYtg2#KuO!~B8f?grwbmQ9zcVHDTHNIp8-C4m?*J>FrV zITEs{y=^s_?{mBwy0q7uz$WA5ja+r6qMvhFCgJq5Nm?g$cB`^I#mD?X&Ybq>BVoW> z+o%_g*^gfJ#;hc?kKE7?57{S0hfPgJgnSYit zo3Mf@cMu%pu7Sd1Ny0`1(F2Rau9EvtQ|XvlWCa>MuE{OfE<~O^&txNG4$v*^Qg9F7 zC#b$e+IAeA450B`cg_0k=JN#}I%0H_uBh8N0gx?6*&1??xKoLLZV}~h#5y|ULhL=} z?;dnGLAk{ABjc4DIOwYBE1ZO=p&-w=@co?c@OJ4e+h&qRkdz@V$2}O0Jf7@vX<;Kh z681F@Rt*z061QRt`JL4MMQ(bT0ZyvvXC+|xgbo&+!`1~q)gY2^_g?=67A4HeYdb1a z>`?iq`{=A(g@Z3CWImHvBhUs8@Xn!J;c0KVjf`9qVNZ;N`lrY00d--cLN3z?kY)1+ z)#gkYu1PO8_{|s9DRh5s|*Cdvt{#w;{BDk6+ES>;9&N(#qGGXT;Z( z{C(v;&tHSW3y8R2}e@eT;K1RAseLOF!naK0H`Z$YsCBMfFb;0X%(;{bgMo>dvLmyEh zN86#bo8-LRM|06f7+JG(G~Y3g_9Sa;bVHw5RKQ*o0BQ#l-Qj06CLgOUtmn+%OCTC8 zb*|QQS7LS)rbG*_7sOJ;s$E?3ca?WxoUW;-R&cM98{d+Vw*fAj@)aS6?k8QMs4G>= zH$1{0u@}xx{%TeIs_SP`8InGSUnvn7JKbN&V}{AtbOlPVC=&!&e%-$NUTWYAzJ@XA zlA7}lC%YhivIg3rHB3N0oGumxWz;uXM*~~`&|mJV=qIq|t>`Qo_t#m!QtB$1%Ppx~ z$si2c7xFDD!i!i!A^b|c%(a&g+V6{BJ(%Zo+W~*TQCJPn?JfPB??WSSsM1FKxc=sj zJFN#&yQ*$AGG_PYCqkA?p*S`QxK>eydUYe)ZT>lKoO9)jW|w+Fm|Q;M1vs-1CyD8Q zdZQ7qj3!}P$@dm^-mNidy>?2=7{~`IF6`LHAS0dYiDIPZ);we2k_~%6@*ZxqO82f( zO?bu&jDgVI+b|B1$h}?=&oPcJR(Q6i)Xr`4o*U;IQMcsq-eyEra5fuv3U%`_OS{8Ji@aX z;)y!F$=gJx=|XKmt;kUT&%G4@<|BY(_6t)M@BT=xegm(xJURN}a&Q4DD8dPLuzed> z`UCGmAO8mIVx_(9Sy4F0*zVjxiGZ#Pv5SWB=U-$&5{_w|Nlw|^+MzdLLm@3_O1H@C zJaOe#fi9d)M|x7F0;&qR>5inp?#_0S^;7}$J}Fyn;{vWjetH-Y+ERYvIy7vIVrELxut7J z7FtLmrngNM^k?lwJ4+V@3Y6XeHT(z`$QV$pzB=Ly{OK~0Gpj!wAn0Fp`#!TUpHB-6N(e5SEL2U(-&6Kr3PqG z@&zx%mL8H@eSjE7e!}nFe=xRp|6MwT@|KHHlt#_EoQe6FU8DM8WM=8ilBv42&A!7m zB0S3ty^JzF%lsD5virl-rOhF0pd!1NHPE%UYoK@c@g~SGJ#a4zfIN(eI+D-eoIP-u zR7X2ke+Z6nMzXpV$M3pdv?m0!Ld_G=TU;@n11?}A`y2ON8Phn6i! zHoc*gLZVc#<4;YEQV3MdTyhlS68-=G3znBTp+8vRv0flO&l-hmSILL z&h5V;V2x^sU&jQBuanRVX^%=F0;1943$Lc(BiX3?ljkXm(Fhz{Bev0Q?$~k(c^dAu z@CGw_D&yg)Zwh-uR*;Z@FV;Zk7xj^@?{FooJLLtRB|lMT?pIxW4WGU{W!*` z&`&f4Dq?Gz6SbwC%wS~gtEQ9lCu8M$ljCr-_G8+VwSURU*sf<1IZ@9}uy?&4r;fGH zyp96Y#{~;a;)BLVXW9nhS+|43TB3;$dlC*c6EeT;Q8FkIK*b6Og(8*{{TI5i@w7}_ zeG1>T>d}~eVP{>W<1D%zs}JZkBBTXmDusF*hS)0Hh_yAXghR|f)vC1{*Ay7cLRW)+ zDs+o+lo6e6Nqq!yr(PE3M^&NG*?Nme=7FIbC}_9nMSQU?|D;)2Ij=v`C=78tGfcl% zKq-l0a$FHP{m%*N%-v6qHLf6UryIXs18o%s_`ezxMpYzq4J$`tgwcy{Lj8IDV|e|D zkhlcp<+|9_OrJvMmp*@fj8SGk`UNzIgPgLkFT^|blgmkumPNnHFr;6WSld*YlU+vG zSE%bbUoy(fgm$)#uO`FLx{q5RNw#=flEz!>FelpALLSsYUXSj5euAOc%89XUP-n)W z+SE8S5C+8wB)+5QO;vFj_`t}OM5of!d*2DzA;<%?)>!xcmlSaaHHre5AF`|6EEbTq zVzt|dF?ezf^jR|0dy~%)8hfc_*m8M?U2oA)Kfo5D$W+ThJk%hO2~-j)Lcc~R(X4l| zyr3i5+37ZQ5wr}M^aq+t>36*u2RL^JgT_({<&k?sZ&7rL5~!PorGRwPbM%0C2dyyh z3YScWrR)AYg&hhnI`lHy#ilVhGRb=nb#HS@to^q30D!a@C^gxlO6rV_+)JVIU|mo9 zc}|HjV|JEtDKR}p&j@&+-=uVI@j{yZLilag>~wuM)#0MQ_LP33`y)HaNx&OV%X{+Q zJQZLoABY>U;r|1%{0;dZc*H-46&xK>NCy=}h1Do@V&C5Lqa|>pjm8RAPQ6RELPfg@ zR!&~WNsAJOWFvsLoRWKRrVBDA@KZo!UT3)Wp?Ty3jx$}?0VQq)mk0!9&>Qs6cD|ay z0@-_of?kP7!j58F8FY+ZLII+Y@eqq(pG~s^F!In_S`oiNo#zberJ|Kol!jdRmqa+Y z(?Qdv^rPWbzoOrG92mx8cctJM)B7NP5kMQ z7ArbrTVVg-2a4R~T0>CeSJTv8@IXQ5j?wYRx$&ZiNg+L|pDi`libao{ZYm6awU{Wd z-7&W^3~ov9c%S-$D5FOU>A=&-{#P#?*SNV~ok<5@pSh8FEVa==Il#i6c~ZZ97r%%OaztFyYt8;M#nB;pNzWq=qFDpi7Uc#^W!XWo73;N^k{LyE%CV^#u3{pZe~sBJL$vsJ zZoR>JN;GP~M~|_WY=nfIV{Zx;vn{s;g>)H}wxhRoJT0^$SPoi|$&ADWxn7AiY5=Z~ zsOCE2>A24F?CirW1(&HByI@#P$_|AA_1t?}Dr)kezozQNC+vG8O{%I1Z>KpFy3S+I zNE6|!FarjYmd) zx`jp&-TW+5QasR!-Fo8W{gA+-5wBh=m=`p!s_3+xO+8KK`rydvY1 zr}(Tvhx2dp7Z_yS#4`a-m5C_BWZ@>fH|ckB&Y&=@tvbJWjXdi@ju7O8X|Uv9uenn$I;phb9BOE>7I$d9 zu9|23@JQYV9(gl#%Qe^^XP4aR-?p$PbxG{0drTluG3FFW5RfF6PttD2&l*WaI&6F6 zDGh9(F3!kz0$|(H*brdNJ|fJR1BO~NlKmMGPTCWNUENkuaC*qAcC^KFtFfOXOsGYq zFqbf5NWDpC#|1=IT=H}nS>Kxt4Lb1im?-I|muehECZM7`q;VdhO}T#2~VGmluefJbEHVn z)^4G|u+|ZBrr)U2>TNntq<=RAc(|_j;vI7yN28se`$JKJM45oh>HywWU-ZZ%=kTJ( zxWsmH;b0BA(SvX%{$Ve(rUp5BrMzLbT%x?a?3L$r87FhX8pAvj8|3Ol9akMmbIq$u zzUfOHipLq}`CMv4J?6vC4tbkm5+Ipj^?(Yvb=HW|MzhMoW@HfT^EOYfjL43#&$Cj} z$w2~uh7;Sz4&9FJHcLRg)!TAgPoE~BqD?h6_g8M^rzJ}l-&aK}RObL0M>PZ}^iEko zKtLuG`mo_%h|TSK4|Sg4h4HMPvb==bwd5634<{0b^Ni$04gzwfLT87-DXuy)t+)h3 zPk5U2rpDU6{6$oQ`Ua<+CDO3E0*>Ro4mDWVIrRZaR6*dUJF-);ou_?YcyXm<{c!n+ zGd}m&^{}EB{p!+vOhlSEyG~C@aTX9cRgY>>vSfvyhjr@@;Y)tUflr{bw*5r_UmB z$?`9X(ZWB2QO|XLlDUtzkbTEqxVP{80~)jJTHYfd=n1D0KpOI^!p-j21qJl=4Z@$j zY_SoJ5h?lpi;HDTL-#rguQASK=$a1J?>OFo|wG$t0CZGj1&?x{T=cg-a*mJdeXa2w^bx()TJ7>P3 z!EgmwFW?#n3rTW4+D^Z536n}VH)-W-foEqG@ugn9@LN`$|OMod@UI3rXk+;)NH1L9mgCIW$&Iu3>yf(lB8S zjbhaf&ZQatvb0^ItDbw-Bo`bY6A~A+;uK1E1D&bh=s8a3UMd(Kc-S;89vsO8jxIpx z#$foDs;Wfk$Kq6nF30i7%N@yaA`YCl>tQ;X~2OS*n4 zRiv0GzR*|}>na@DBQR4q;MGvQNe4iHPSw@Dd8DldZfLB+fjz5p@)?@KaaxA7UDSdo z5?1Oc1q7Bco3s$HiH=L(Gyz_Y{s_L0G80|; zNOg054Rlq7oY*mU|H`w<{w!xaeD+Hhb{adyHA0%@Gm*#Y2;{gqU1&9h8r23)Yn&oI ze03x38JL<^(=8m@^PYZ~Q#g1(P~m*%wMjciIVPiW11K_bJb&W-l_K1q z!*0iPP{`U%^HNPuC4cBlN=Qv)sy9jCu83KWk&@o2{1Dp+cdj=?P-=~aFm8E5F@r&P63CV0x2dESHF%Mh!HBcN zq&)OjVkWs&JGyr4hPTF={fxKmc|(3NdTr@pLA0>hlNz5()%1&>h@X)AE(d7L(`@rMmPP%0puZvj(6;s?+6W| z>$!Afv_|}td*2NlZXz+U^rDrvq~NW}Mcz{4eHy=Wu|bxXIim#(BmdAMQG_(dJ?Fe2 zwwW>lt-b8W?(?%5bxJ_*i-Nte^T=2^$w!w1zRMQbNEljS&o(Ke>o-CTC$>MPAy$CP zGEA49s4&%-@uFq)Bn)07z~n0Y9Nfa^#^)D{0r}<_&-y&7{9?t(SDqVIGQ-POkaYj( zYN&pZ@cw4vm-qC*M#=}s_lFZjK~pO~A`dX4@Qy-yNuo@{>I9N`b9^6NL|hLn)L%(7 zF`g|Y*0jQYEOaa%bZF#47PZ~Nqx0H6j@F8XbGqyBCE8M6as$<9DTUv@-Z~_ctu*U| zqCg}tV*h)W=O(pPa}$w(a=o$OYF@N!&=oxxoXBG6rpf>{L(QT??Zx|mp_cM*_@1#U zD4Vi0O#E)loGKh+ehcuY+rGW**K3k}FTH6q7eIB6!;URu8Pyk&p>1P?W}on_9Ag_{ zXruvf-TZm#4DCmi2l_C@gzl=JzV^>9K(e2kFa z`CU`5PO@Y`p=w%%;I7qfTug>-aEdHr4P@e#WHe?ec9iuuTUaoS3x0mo z!|AEXXMsp4t#!57rw;aHWTE$P3{@^jl!S)z*_v(**T)x_A>OKC%yUz5l<0cmOIYVt zbFpZV3khI}V9y>DgkJ<-KWVCcbgn*m$)DTG9g?$J_|F|e^H02^`)fNm?F@>)(nk9+ z4n3I%6LpI?Z^(=+VR~DpFa#YxKvF^;=9T&B%6|cR@)?)x03hx7PHcoUx>!MRq~;pX zT09TF4GFu2Q`pDa%JW#lP=XE)rSZrXd1qJ9u8ZT0-ot!+JL|QBZ%*U+?!d8X$=$O# z$MDCF0RiCuog3#t&asblzX`%%|GjX?TcFA>6&-F2*F8unKc-AUQcm}Ce*c+Z%o`cA z|E1^Q-R^?wB1e9oc#aoY52xNr-(-fZ^Q2$pTAMO7rWv#TT)4DYLl7_(N+fPbZWT|T zO#{SoNn4g%kt6bi;JuN5Bq~Z|j*0<{{Dl3@-blFM61;gcNhDO_e`qX4@3yDd1`wBEorh$_bLBxD^z$rX zSYAmPW;Ch?DABM60{ekg+gaM!J6`2y?_9{CuVjGrnT?qF0~ny@Z}<>U>~#hZ#_M>1 zrE|5^m*lSsiTOELY8qmrs9jN$T|7iU$7 z7<@9kOZ=I=bDr}zEi~%DvM2Ky$b3?Nn#Q}UZnX)*T&s`gP@Gi`gGvTBtFG6$}vfXOL zy*NoEq`3>}x`rn7B%2N137;*6{V=_Hi;)SVR95o^-Rz}kX#VLYfCxBJ6oMO`z`njYbkm_6Mtpu&Wxg%56X0V1;S~*8onJn$G=x(kjgP#VzKh zCIqPKXEKY=nyB>;fWvfCN=l=`My`_z^e==2U0M%6`%deh-gS8RR|GkpES+#Z|L)bb zti2F{hMZRt62yAKvX+KvhLORp!wiq{(>pp(m3CyrRlUs&U&*Kr=%JV#r#f-yahPAI zD7{e}*2#&z=={~y(CoYI^bQ8c$}qs6`HAW#Kw$j%@#!9kg?+xxbWIhxIlH&lRHUit2nXJ>p*0q0uJj}MqLpr(DqytlPDPa z`-B}I3ubwR$>>XMa5fP<7b7bQ6_BaXLal`s(Rm3-1M;ibNTapAT*l+~HC7>0%5F@e z-tz}#EV`1pd`w}Dp1n5tC8Yj>Yd5r9u=*|>--sJWT>vsh%{oF3W

QSRret(U@-s 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

y!VLu`2(-pZvQFc3S78 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

9J=`bqncM>~ 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 literal 0 HcmV?d00001 diff --git a/lab2/images/02.jpg b/lab2/images/02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..280a57ad26115066244081aae233398a755ca223 GIT binary patch literal 78895 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


< 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 literal 0 HcmV?d00001 diff --git a/lab2/images/03.jpg b/lab2/images/03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8d75cea9b38ed20184c5baee725fd9c49aa0543d GIT binary patch literal 61586 zcmeEv2UOG9ws#OgrHJ&VARVdFrAAP?fS~lI(utv$P-6q63kV2E?_GKc5S1n%A}vVh zy+Z;B0g~{=nYnXk?)&b0ci#Q(d+U8~lKj_VC8zAY&pvzq&OT=!PQRVb0xoH(X{rH; zh=>3X!W(cp0k{PqK6B>BhwwT}_>i0@Avt@NgzVfo((@E#6cprS6dfCH_G*p>z=8 zcfeU{5*iLMWl~!Ghvzum>BOJLWuNC#sro=~Fo@!oc;xZy0vQ7%6Eh1BFCYIk0ZA!o znH#clw{EMdscUFz-MepSWNcz;W@Bq-@8IaQ#H8dm zDXBTRdHDr}Ma3o6HMMp14UJ9BAKN=RyShL1^bQS=j3UOqjU(sg7Z#V6S60{7(Yt&5 z-wzHkN5?<-A_5Tq%+{}*{S9B#1isFkJxhG{+z-Bp&iE1@V(POb9Acz2%KGOXy3=xs zKRr*U5|>@|;R2V00gC>S#~>L4x8xiT`Uh)2arTcf_Uu2!*{_WKl`kZKf|!Ufc*N8I zAOO3b6TuJoFM5c|Mp?CQN1Gvanw4D|c<7g3B~eV>w7QW+;VPH5JZ5-M9LDB(3YfVc zRN{KHc?vij1$eZ9*}&e`&T04nAnv<5vfvbuF>ne9x_eG=dGMzy@RgcBtNbakKNaTB z`SSnRTwJdnDH;7*8)|n=j3yh;>>s4w`p|L$mxP{Y9)Gm~t2jUZgeF3D^_t_q%6<~B z1{ojtXLik8!t<^hNy{Y`hDqwUXx}Kq3g*=3dJWZ<(eQ)g77TA&RQY&!_X6Zg{b%M> zXFkA5vCe}c6*`li5Lew1;rsyOhSwd3H)F*afUpOKuPYWlaq*S=eV(~ei*s-Bd^k@< z-e2U%#y9e%EU8}ZWdp3LaZuuEV_*xtHyZU!8 zb&67K8+IqU}xkFZOw_^ zy%3nLL$B67Pokx2YV1vH?t8htSJ0_1g6li6> zDRt3#bCzuZX`dHtb0uV12-0$iNE?o@xj&&q*%!vT-{|ldMtog!RURmeu%VDBN zq)2Gv%rPy3d$hss6mWGy?~wHrpyCf$t4-(avgDq&j7&*ihAe3Nogu2b^frkSx1&{q zOU{7~lpnEwKVm-xFy{?J7me#q0hN83xHDvC`vUh9>2=fu?FQL`4Q^5OArvOYp`*S)=g(RDr_K4(V*cr^ z{@?goz5}Z;SQ=Ovy0s3(%V??+d@Z(g+j^1Run_yh_uGOH*Yek0&#Fe5+r*K-(lCzF zAi7GC0eXYDG4d8VEO`p}O5(#t@bs_7a4>V|>dTE71ay&y59g_e0~mC4iIvKGze5ds z=9BL?G3{vUn0b4oE34AoCHj{<&TBr+c7t)1+|Bjj=cvfa@=hKk(r4q6=io>9Q7DQG zh?ljLTnN!qh*pH6;JNxsAKzZ5Q>l$KY-0MTBRr0F-e#B~MVTu>DBVLEm7f zev2;mTQdQ?O1d-<0zL$=JJWdzm$crd&}bavMyHY5T=mT7#=PH-9Ill(#Eod>TZWJVvt{$ z3eco!-VP2nl2y5pRATAKCBtT=M<%0HiH}U!Jlf0&!NO0Kpi_W#GVu5;{a4JbZeu)o zD%L!|ZaTB*)^t!?d4M6!U{gLJQxQklvxFa^p4}c$I6qNP|YNRMPqmDN2 zh^54Uzn=nL0#OMNtCJ1}Gw=SE21fBIQ;R$0T`rt=&#N4L`<%kH6SUro?k1=z1H_@* zzb$zVXSC4nFP-cw6r`WKH1UZKd{@)h@tz4f9DHhk6f5ekBHQ|4B?5j|y-6M8G3zr9bK zH+?~Ka-3HfB_4zTaFaYmmT~?SOI-8?bb? zWR+04LM;2|$xnucF_%P%Wh?o{q#|dioyVFpviZ`Bta^v_c)!BZ!|iWerih1JHa2uc zYk|_)b%v#OE;_aeSV}1k*tr*{g=9<F@Q=ASh~J%wuiF=H0FxCK=jBA?s6_B*o%tW|kSYD_6`Lpi z#w!}z5Uj=#C3K>^0sk_lNzpI|KbR2cqL-A}jema%u!o6RcP01j%3ja>7(1{hP%f8m z^jn+e_&duq#*9!^)&A|Y$^FgjyGgBYu>JQde?5EuKI}IZQRcT+jfkFJLrs@4U`dDm0KYkawIVudGyN~ekaAtHveuNdIBe?g4^v>c) z0z&Ih4eyo6?&WSIBRi+t>UI3wd7hQ3yU?4y6u6Iala+5piCIdbc1tgM#@7p^Ur#FXD?Mu67HUANH4EdP=Vw2rozT0-pk3C29j|cCX=v!{XQ4J^OW)EK|_~8l({^Fx@ zk+mt*0hfkTA8(0+2QR>UQ=-5Y;C1JL_XAJbcyL)QI^NkBdu>)wr1mXe;jVnyh)Xgh zoaYQ5!+JPNqH~41Q^AsxMjKF_4y3+8fMVN8u*2IygGpq+jk|Dbhp?+V`J2U1Re?|M zXN%9hQeL2^=RT@am7L!^B;(I{N`9B`WE=;os#o%HdhkRgY7S>c6?7Z-1O zFjXEp4;&PPBRB36<6C~#2OEFpEGmUbvHW76jSi# zzaRf1bJpn;;Ix{#cSs0PK5-{Nu61DTzTVLZ{1-8LR=vsYB@!SS`u}i0=kRv!-OE{_KzUwPndr+<(I1J-yZ#_fcefo z{v8<49(xpakPalYsr846P(KLLTMS#kKg1R4{a`IV6^iud!{TNLk=e^otbgp5A}v06 zhY%$ttQr(eGap3a{RmPS`HLj<|H8x@sMGL|pB4);4>Aa$RG=?mL2!-`N@dGE(fbja zg%LutKb9)7Mh@RNf1&>2Us_Ws+`WY+EH3cFg;)YY>CplHu3z=PX|inKohkwP{*ba# zJL8WU-9^VCe~6J^yZ&WJ^}my4_E2?W+fQ^e_XbV@JcMQA?+1|mcY>+;wc`^C>mL=^ za@Xnq;8^^n>%WZZf6G|>MU#FN$R8gI0yg~b9gE-(KPy48(l37$;RlMbudvY1z}mH+ zh}*rV_cv;&|NFUCDzQlo9RLzE{@t(aWdA%r|B!0`tvI0$BY2Hq;4=F^KF81hWFFLi zoh5`hzA_19-=o@+`$6IB;lD8pBhcR)p3lE(FVuIhUF{%naF*V(AIY=X%M*^xZpem`)Z$o%lt zOZ$&ewnn5irvQ#?z>QSs>X{a@b*)lE8KsElQN^mm-@=_nMhlJ3-PBj51N;jfBA;%& z;_@lL4|w35lv71O(SGo0JzQ#j7P-G}zDPo;ne}^EL0t+@>_~O`d#VnyV^L!?@I{MZ zkW9or;rvP8oN2}5z{m*>R%WIb5ArDAV`0rD4OZ7ZttAI7z1$7*FY4ZVPW>X6Os)CEgA z-<>uACdee!93>4g%|CY2OnG#bO-A6bGJUnIH7}pfz|s}3e&4C0EHeqtR)VCQ8>eqy%Rr!aYZdEuUV^v+W-!o=(7s+~Tl*u0=0!CzhbMw~N7cZ@X&uHaZ^ z)$c3E!CMz>opQ{G>MS3djSW?;FU_CET7UyO;vf?D+ic~)T{(7j#_`hK$p>L7I`yZ3 zDZdvE>50o5E=r_WstDJYW`_}2K{R7`@Ku)44)kjKUATti(aR`Z5tplW4$U^w^O`{s z=PP6RONvFn*58-*I0ddpJ!tFser`(&WpLPKeMt%$u0%Y{-HMWK&FNn7%U805A-gsi z*3LU!w>53B;}UIf14v6)x^A7Hq2JuD8^GM0KAxE+UNu^KfVArPdNGqPi5-C7oML@; z0$*APka5QJX3{=RWM!>?w{S9v15)h8YH>LB@CnnwOXGLSnp>%cV)SHK;r2jOjc8DO~dI%Z~}q0<^;IYUo4^uuxruqAw;&b6Yr%#|a& z)u(tN*KxMZBfqZcYf8L<2B(0K@GK0K4!-~79G3H#X%M?p>zC=&G1RlX;Ji7I7TvPq z;^EBW%Q$@sD4l6CYsw!Q*fsazz>JHA6<~T6>`~P>GnnxcFga&)0LO{U+Y<_t1^Y>x zD^(U6s}hLU-z8t;v^UWwjeU*?<@q+@eye29s9g^vc;NvIG?~R@| zETYKoqe_dS*rGTEAYBax@$!ytfwqHE7=+KRSHI1NjF zj^l;lGJ~pmev^sY5fE+%rSH~%30I6NDi;N0w+KVdFBRiuX5Xw7#CZ<%%x=9;)w_O4r9*O{$*NZq4VQ^LeBP%{P3Gy93>hT{5n1 zRnZZ>+t3mj{vgp(sdSI(^D*uB`RjM$Fa{+K#)%ll!Ix=C~B1uy4VY^|ZJHIaAV#ytwNe4x=2@qAIAT#fXHP2d4 zW@K?mDW0wy&B%#kmcIVsY@r2%^00!-Nh@e=NGv`3O@YN^}Zv&^ra3 z$8}X7zqrS5)JFZ%K9-Lg{9$9A#9N65&G({>d3^)qT516ayE9TgO@8ub%Z!LDK8xN+ zKD=vlZ!nC$2Lro>a!Jk~Pzs;d^iD&(IlqFOhkY3krc0HQPH|Z1rf+t@a z*SeSJ8jyDiFvPDU2y=GOYU`r4{$r)}3%%`?@dRn#4H zw#goPf-iy-U#3Ezw}>rJ(XQb+s>h@P1TQSty{jME;iyY8(taW#G^EP;bg2|u%J_LA zc*_Ep5onkXpBM?IrUg!uU3KeV{_J4>4E#^mYRmHk6SRK_yIQnKr5}Ox8hb1aurLAT9`Wp zbY5ZaG?Ej5BxsLGvQz3*q&w)hHGr<=H)tU5gKVFBX>_LRzxaIfo5th$LQH2_pdu!1 z!8{EHrBN^F$;UiY*s`L$kZVn|q0!NcHwbaOLkJa(X}~`uZTf0TjCAgw5SMm5A6%=vrcorsQ)`P*j3LCSP z?TYf<$!y%Cfw%lTAI@1*tm&0Hi<`VSx!qe!2M*Y&Egwe*bzQ~J2CQ8}6A1Zh#!?GRx^b;bfEyyww= z1(=DCQ2VyFAQ8ue+aoVZCEc|cMJ2Tc0RuGeIB&W?86ChHp_4*0DO)H}CpSlits`&7 zZy%qSH^i7cY;10VeEDWzs41Q2o|9y!^7#UJjJc+kN7-v1oLt1EXN7*Q4g_q-(V@Aa7bSJf z7A(^&(Mtu{o~~tU@gF7oWj@i6L@(Idr;q0H>Qr~uS;_(-Gnjj5&kt-g7(QS-<-N`^ ztu~RGmdv|j!sUe~f$|bUYJzujfYP^5C(6%$*CAx7vdR&?zILytPC7P*Tw^J=EmFlY5Z?(Q!LG)OP|RuU&o8f{7`* zYH5Hno9K6V%ko3m&pcySviB|b$fQ>gTmZ(-P`Buyi9fPaw&=Le z_4^!aZZ2037N%TLyW6%pm@8y@!HsFI&%=N8-s=T}?>=LAA}Oa2A4g6B6-%dpbNB&X zy^v#Iq$rka0eIOGV(pxyeu0F%6LhReQocBBelRjE0-`$ny@ZbExAt08C}}fz36eR$Y_BhC*fj;iW=MrUVA28FmwG zRhK^@(b}50i5RSHoHpdPBY6k-LU_}UI= z+lqZvCT3rN*ww9Ubin(AX?lc^Bo6pqNd^^oD1IX(&|2iE@*anN?n^Tj!)D^XsOkzx zWww+n()xWcD`vvMW)^3#-9FdzRZ5scN?zWSMn^74#fdKo@^xN?GcM@ZWS^egW!vNW~owkf#qaQC(}K_4W4SiUB#kVbR z`A(XeU41!(@cD*K;kbU3ZByM*Fq>7FJi6~7a9D{dP!y$Bf_yz@B`_l5vL)4bOQ5dx z^`MaMRh`oGRh|is$pw$Z1^OjC0OLEa7oo%*tBk5hMb_7F8|c=@^~N!r&yOL0XY`mN zWI$oz6=)2973+;!Zbiv_I5x_h!;wvNGRQ3*}k zyoDXYQ3YbO?7Dz8Y-zkR=x}6Z9+w!*t9`)C`E`@GX5`AF>JdiE;nd!BPr=*u=*jR7 z0}V?iKY`9K5)D%K)KQe{SXuPTX4K_m|N6`_v$g(;{$gDEe(AGKxC7{s`Wst2q-7`_ zI(6DQoaHQ(w2cnq=WTT26FG0*uYI?cTJvSLYt3s9_o_zI8)qBpd@}?v0}FI+ zAS>I9tLXli@Hh*3G>9R%bk0fs)nP;r-anW{3ab96{EARCkn;U$e#~T3kKFh7f_c2qm8@8}k zGiiC4w060W&^RvyVbPh_&-tE9CryTrx@2a*;E@cD*j>$p9J1e9pE z8NPKch)?e;+lD?`T!%M~&iK;fW@?GD++bXZVQyfk_f?Ah#rAxxV$;y$uy6&-ymfbs z8l?f4VXMp`RIt7!hS#Uz<`EahS60pN%Xz*frKwq0q~I-7*asaKqM)TfH56_M2M;mh zPlpfgEJki!tfqPm9+#k;GVQvS$sQ~S;hJ}dX}JQPonfH5;FvUj(jl>7{SX#qwqY!^ z`|1N--{E|yn2>X)XOk~cL=mF=ny4>fd+aGLus5CT`ihoWc^d&MjO^{O1>QjUB+O=p z1>YF{(wgr$&E3>e*D~aNAsf~lA@6rr0kKT3d*3ELglB^UbY;k% z0$#vOVol-~EYVZti3922f_nPL9W^QiphZC(-5M;p1(KTpdthJ7I6o zGDdHXv3*OlQw|!XpgON93dTy!1zoEwWS|kyQ~VtB4mnZUTwQT^b(_E2`8r#j*1&9G zv=kL1B%odTSQ;15A^mDy&N3O9&E`dus{n57sq={F54(B_sFpMKMA?&~8Q+V>u#n`2 zjUa~aAZ!}=D0y1TwjJzF0p4~L!HMhaRKeE*C0!$|&jnvypS}^P#>R~LUJ+Nc8t*jb zVHs4wwDi;flftkdup*yDw<5`|95Lps4D{llRYTM7;uG?$Stkal)_gBH zGtA4{?An4tz3-addr{nc8hWl zIN+=s&tgPBW!g?dy7W~6YcjX3TK)>iXosbzy5%_mFYtDoB^Gy~#qLA`LPfywu}U-$ z=)JKz9hI3*H>rZcVyCg|Ek?~x(mqjLTy1lt0l(h{yd!D2n7Ef2r63wG3uOdZv5nkl z8xpmzepB3AqqZ5Pyx*I8EMtZ)c#8h0H-7SAnr?7Ys17EZM7}h<0G}yXUabnJ>?mWo zUS{WTG)Z@|E<=WfBWXJrf;lk@=yQhEDRwoDh>E4qIgi`nE7zybFJu|WzeVix=j0998hSZ-(anH)pW2iaX~kG(bWHp)Bv~Dl5aflpUY_2;=rTs$e!Gt@%0T&f zVo>9UPxPC3I!rlg@8cfi(S+_u;kzNP0a|NoAz!0ii4P#2nr_xBCATR1pFVGdGD^jv zpbLy4EsR-xY{l+;$h3|jD{$x((xiN`-Z5&`)Q6O$>FhRFR&GY&ZtVCF-ln(Z7AmsS zihhs}Aw@1|Q(!(B6c2(~Y9ZEWT({W;3~pUa7l;jMW80X%UIS|z7Pet9vNHfpz#731 zqSJDidE@R^Z8iis1Q@3nsSEq2|0D7MS}*K$$t}vKKvX7)qv_jiai*wRZ6J zEl*FL*Miq1xo!HZJFq?dEopdTY!x2i(b9Koq`=O4YN_pJ50 z*Qu^(y(ASaXKCw~-~E1uWg;l{^PsqkOZ*3Ymu!3OFv~u=o@EJ@4$p9(y)F{<(m-ak zL+EJX$bN{U@OEx3Oj}W9+VnvjeqD>p;rdFJ15=-{|ISA!n`3f@Afk*5?j-6R%VKW* zt*c5iC7brs(xFXEQ~RYL=g{(bYsx@bL@;MrFh`^reC)g3^G!FT9&?Rhlarla%GYa1 z<-0|;Q*R@5%e5Py>t5%+)&#ArVe-4!o%M{0bs3Vnx_K-=h|8lv}$D-_&wtRSu@ zP8D{wgMrdupchwyZFThohxlR^GHnmWJ<{b3eceE2{n_s}lo=*grL6V2)YB`d5U07^_ly;A)ndWhuPYDCdbe4c&G%a%;sl5U(D?bu z`vNqkq8!dfjg>~WW)iE!GZ@`_R(9!G ztGktN&iYr3L#Mm-gr&2=1oBHi{g+Jl1M*@zTT=Co3{$T7>%j$YMIC}Vvsx;`U5O#si?&r0Zb2ve>uscIvrywDsqUFmF@a+ilj~}>% z+^o4k54H+7W=QK2G5JLhVEe()`f#y~bXuOO(^kaM%agZ7d`{j$IPZS1Iln7Yz}h9J%_JMAnl z@a8aC$CNFm7Zjdhf*C2~rZPNLWe=HM{G5Xa*p$*XVUm$f3>!=Jao!+_D;=4@?Odn~ zA!N)IAE6!CDk$AOyA$cUU)!o9E)TJ^-yJ$Wm_|CC1&3?h5(?)6D_lN9;_c5KTPtr( zhUKqLzB?fuR@r)x&7QQ-8@YAd*)f^>KVOkNLZ(^11`5z)82Auy1%zGyI_T@?xp7gl^!J>xL%jpJ}`#oik-3wS_5Li!3%?MXra|ex@8+iMYI&9?%#lz#^Gq&p&#q#_RPfC^H7!s5cqn#>H-tV1?`463BqxHMs*?k9fZM5w4h zR_VT#?-G;@iI(XumqzOFwI{IzC(naf5QvY6Lo-f zqFs<(gu$d&df6aMsBysmYYk>?PCG?1ZM;sx;l^Oyr{(ahlqlNCUKV;D~epEWqCJ9MvA2U|s zL-8DxHPiJoqO7l)tio3Sp1Sfp+{QG9{re?FA53& zD4?{ura(J4LyD169mN_*39H{7dn6<6?~?3$S3jIX`%?Sc)Y&$EOn?e1HazXtps0uxUsd=6cw5qK$EBt&?p>9l z4(InVh0EKZAu!fy*7`+i0en98taD`f`eAi9L&8I;x23pra_n%j!_E9Bq&xzi&-?wx zIvr47vgcPu1Fw0*^+xGkGP-->%sx6SYq)KMl6ffKi)qilw6eTQuX@p1B9f0!vITrt zX)=tL2};(jug4E!p5@#~lvZA_e1($6R^eXHLC0r?Tix2IhP_-bL7Kmeby!_ZXqS`f zh(1Vvq;1>^vil(a*?lfg@!Vz41anWV$uGQ@ z8Q)!pqd6NhPk6Zp;zWppPXYb!j~KUIR`UI@C6QcZuFGmnA%Qk-n@(L89}{Kh-D~w# zO;b{eq1_*Gg6Rn0U-D~>RsZc*9~Yq0CI0xZRTHX1v*65Pn&IM7-3r>qf7O*IFdM@0 z>^*2p=82THz6s&5c+1QVAHLt<>(3d!uAPLv<&K2a6~owDBkZ~Y>|5k66Fv(Z@TqRJ z5vi}IF@LX|#>Uqq5qzT@aSRr zTrBxGx+>IgOhNpW^111fy_x4XFQ|4#9a?F3q=kMCTEe3VSG+`?Fzn$Wgd_ER>IcFr zSi+Gv!v0#$6Ha_*3*nGF5VJdoizFPYSAe3IiXF4g!>e3@E3tTmTIi!`=m0y`&)JOs zjOeEMz|jIBek08Ots!%-o4~DE(w|NLI|9)_O8p}1f}AJ({drS1I6b^VT``z-;v2G* zf?kbqNzK#LQqJM5Mvz6Zo-{9>f$8dT1Yjcch%5Y3soi}t=)r5~lz$g~=!N@%A8_Z8 zkmIR|%csKr$UTC>PguU^rWi+PvME%zNTxkT;)h~Ss`YXC?1#2xdRzSPm1(^b+$rGO zATFQr*D94;cW%O~-MVIeG#wwi#eWKb@jQY$sS+-x*@e#R;JXOl{ibTM1@Ip(J%k^w zU?0C><9eb6-0o5TsfQ|HEn%lA^B3v~-&ybN@Zs>MfKA8$6{UY9%ZyQNz7=cuX<%}H z5{*eFQXe|dl6iP?3aBD{C3n*Br6+}5sI9r)kz|67^sHd&8D{(z?H{llXI{CNo1Yk@4zR!nW6R#pS}U`47- zNQICK%LOvIE!*DuO}3+1gz7;y?f4gZCKoj(tf0hiv_NVfxCuLv!`wT_`5LG*w*jTM zfPm(M;KxP@HvZI`qUF!##^il>DC7M&lqsv8Jm5P~DVs33?9qG4{k2{$+-YYV*a2jL zJGnACA2ZYxBKt}(C1dT>X4ZKTMQDi2G_JsbwNa2(?M%_)E@&Rg0@1d>K)h(@DW%FL zEC#pgPR?V$EsBp1B(TPpe|Yc$?4s_EBN&nwAdQ%#ovvhiQI?$(7=0CJ;_2m6@*LSqa&L9QjTN{RyW7CGx6z!T}}D8?)gemGsm%t?2jx zRLc6=>_$<3k3i<4+4_50d_9>+F&`T0ji)#PMDl!OG}YbELtR+YyVj6B`hM7(#YnEV z;tllulxjlI?*Gfok%A(0`$4nBN5Z!$Ew7gOK=J>@L^$uRz-%IW-vOvW$D`LD1qqFZuv zxP(w?LP}dMe)BaUzK$Cgr2UOrOa9)={I2x74WNArZ+JnB$v5%lOH9;XW7FKYMvU11 zp5CMK&f=lalv9j&gha{XV_+iI&p(;_(7~_-{Sn z-#DanH+)|^D-@~!!zcoOke>J-6erlw{||~!{J^6#moU21CX$L!4HU5BC{MVHR60jE3b}kzJGszKRJHF_;6Kq`jUdd1d* zxX|jIX6f*#mU{)0AeMqy2ZlHB@%rYV>RN*?6;}`6%3Cci@83`DQ8v0VqL(x0S`{4O zs2FQ)AG>N(5u^L}FL22ADA|B*rl4VX5=%1tAe$7vL(azP181$hXsrVI$`;pI8bT1R zZaIZ7-Xq98YRrna#o3A79~Z#pLPc&6*{bK;^BCu)NHyCKf>oUFw=r!PYa4kKrU7ey zcH^ozWe;urHh0vsJVAbDijLxmsF4{eB|1U|FSTo4ZPkcWkFRBm;~mdm+Cz&X4+?%MW14Cj_r<1XM+^;I}`hxdu>GNs$K_G){SbM^}c5r{2U#t zH6#=$>i`U`tQ01k4eawPFz+rsa-5iyi;RB9E;SN!C9s9))0UO*_*cT!IH|Znv#~kK z^5n4XIw&=WQCj+)bNre|PySjaIN|~q2lFyiT#O~Y7r^{TmY9BY8gdzvI}2~esU06l zrk~TUAIXEk#x1(Wjk__r%{p-9duFg=Itwul9ljadwO?HsEgKtQnSpXe zF$k}e>URTFiOH&u-J;nvdg!LO7M|9kVxs3j{nS#zqzKkGu8%WjORSjf#ML=p752tU z-!NvLeGuVl|7uLF-};4_dZ=U&+lmpI=e1l$digr}+zBlt+crXaAk&L3p*G7A)vHON z(eD@gQb81&1t~dfKRnNdN33o3E|$PZyMui)Tl>Au3cGZyu867*1zrm%h3Kgbmm{fN zK_wbPI$T=23Ki%wzekrT^W8(%H0NU@q?JUPn?@@Gfy#bQhTN|b1LhoV+T+SRxjSa7E>shy=azKx@N1F|I^en9@vA^gk*w{rK46`@c z10xJyodWvAdKcX0TN2%CA1wMveZ?eqjp4b6_WQ+#SSbbXMD{-G{p#oL7q+9d0&7}q zDDHG{ltHk#G&p&eI6;xfKm*docYFQtjFx9<6%>+wm$B;?esLT7!uu*9mAVh8`3G zkZeM%Bi3m=VhUrZC@y?@5Pa{@{253f0)Uf4&0rbG)vZZCdK0eiSQnnJiv?AryR(1e zc2hz*aNa(<*I;8)pl605Ua0H>Ll2%|(n}y?9i05GqA6SvLuUd@H7-5mxU=`$65Q=Y)ngd zSn!9S*%=znwO6W&`EPYyxp9kKxA>1ftTA1=o~>8xJg@ZJaeu$$wV&lP4pqAQPwoPQ zV_UlG=$mHCF9N9{sa;>@qKpDQk9qjS(6Qzbg33hJh}SdRe&HUqeo>_`)-(pyre}`K zxu$?PpClFWGR6B*27NZ3^tFsM$^y6K=|gQ;1vJ#B&rf~Y7S!u7OYDwTF!l7LxT+nX zG!bC8n!ByazDO5LUx?;hpYs0X6~Nb@6nTDl@D$MUp+=B!k4&CDFxswP)SEob{Bn!5 z=agB$Cw)sLh;%^det3j>LC9w00yjfv*hh<(&XvFmG1_k(xzyDWN_Lu2Rq%{z1i*39gd^9I3tB>6C%rENaYb=~Jzcb)Ug z-yVl__#C{`l-zcUQ!5)1+b5H)w2vhmq&cY1%yv3wl%QJmy!Y-`wZl><;8SIV&nnvvQ_<{zr+u;rQeiczcnR0lWW-0C!CO;;*w&x`ffA9KH zufB3|d)6e@Z&tkBMU;plLS!BU!^9?i4J3(e@v)QI;D3-;~qR zU#s}9x_AG7d%I@@*)fM{I6}mAyu>WxH|f;BZsy39pX)f*jm?=E@5!5#Z4V$$Br)y0N+gQBh0RT`v? zc6g$AF7GsPJ$6te9ZXsos+N?jZ5b0%D|SB!U`RKc4`k@@I~wTQcqY&~72O}nIr%}7 zVzBIq227iS*@&$~?V4RxQv{Mc{AwacdPKWebRsv`&RLg5UwQ#CaNFW;*Usm!XrGp8 z0rVAo-n~A}w?|^Lu_D){)!y!Xd;%ov{xm=w;|7l_&aTN=M}XdxF9n!L`|al4F;MI# zZ#fun4%;ukb`qlixAi&nqsl7BsumIE>$;h1s7v522L(>sw(4f;NEzuka|`Z{Y?Iy> zJlb<^zL#f>lP9n$()g>0I-Yl=@>}AT0<7L|E5~yk5&XLyXC@4Z7OAo-0@P8<<@3CW zUghF@29EVd&xWQ7b`NRm-tZ~E@{=-rEYkLV>?6X)w)%YFj_`|sPoJXT#bn*|8)IFF zl98lv5XPS^NJyATUeWa!+f$oTyAd!Tp!;7Rd2G0Gwj@c4l#{^$7GAZE7SPe#FYT&xM$D4Z64^mW)^?L zz`D#tU+xEqBgRx)s~d;Q+j_u~sqvqm64#_}EQ$p0L22-kkmp@2GLhUe#}$zfsbUVp zr1fC~lbpC$V462b(sq*Zx8f^cPnlH9?UXC9DWskgeTTK<(}L(YyI7bAgC0Egh z@}-E9iF@Nm-mAAA4VElmSy(ZQf#H;(>WG4}2UxO?wOnO|HA?zbiZxrFsB`4q;q6*j zk8ovqesy#7)A$SXVxOL0cJn*aBx}bsmA0pz+Ud`4-F$=in^#WWyPy6eQXxq2WqDR( zfL}b){@UfwQ6-xu7`xcyM3|qZ|%>o z|J`F7f911xklC1CruJ}}S}0?{QO2mRP~h?{iNMwNr%&F~HUNw%c|UNS-&fi(oY9H&Q(hFd>O1AVtOv58h&$=>1l@e zO=cwwz2_9|m6xkI5zn4AD9~qj&x_KUj8BS14EUvP>kfW=?iB@{6+A{<@j7Ox17%^< zt;9Z)))YUkj#f$BEywRyyR3TF853u))D-vkIi~bY$6ou-0vKb|#d4duDYFIR=2zwY ziLf4<3+nRQ8WYR|%KwMGw+w3Q{r5$w(n5jaE=39@xI3j3x8fQoP6!DEciK{*xVsez z9w?CDQrw|91Zk1rPI39&{Ab^}chB5=_BnI*?DOWF7kQbLwbtZ$p6~bjF{Ewd-->7g zZeHOb;xe;K-s)gO7 z>##!t|3l|XdRTMNSAuz3!&3xSoiMUmMf`h5K?<2ZmdNnzWbKg+XiSRZ(NTqe)REXG z{uRwuSMP+`LMt}9R9CeI+_y?4TA+Ww zO}77kw@KJ3cALxr`YTSJMT>rz6b91x2yGvJZ1~LOY(NoW^|d=bkyWGl!cXp%UasQ} z=d{a|)x@%zTJgz!{Ap6DLAkat(jxWjB4Z{9-(78tj7#zSTAJceF2HqJokfc#gsCt% zBq-hNB_Eyj6PQ^}Inh_!Nyvfw=G1DVX)W1xz+%Lq5UtxrVLsE#_(pYG(?bv8Qpz5c zRP*L`h&aU(GThxyQVfIU{%FHlC_{KoZRJBf0y8*yTdzFJFWHfsBL*jKWcZvZb{p;O zQ8r!^&2e!%W)LDVs;SRbeEaCfWwQMXkv8%MRKwIJs&(@5_`;5%)iz>wIDwxGO2S8p z*=q$LEIvCm#r?8So1Ogtw&2^1By>N9)%$3)8FD zHr6Uy2uMJKJXq7;;o8esT|Hvoa9*|OjeY6erFd)>9aOeeA8eXHZVW{Tvqik2emedp z`nwY!FujY%bLJ$6hR4-p7kf97>55b_%G}BAwA)kLEsTW0jGfMuq0_?3Unh+Eysah+ zyrNP(Pu52I4#|>7U2~&-JkSe>#d0vq2$bq;f&$)Xp|PLM{s-Gu`6ec?f}L; z$IXCTM+b}a`g+Vs)VPmR_GenWsQvWC&P9@J3NfVCI+vdDms*C?p|O=)f}j!n(&?~+ zO6~P$-GTce5H3Viy?HT0q0F0NNcm_aw17URyhL6)VUgZ$d=)Ha%bvC*QjJ5xBAMkj zuB2F1a(SbksBz4O$_W!91$%=Ipw73O@7PE_T9QH58VGi};Bxtw=&ml>xB%B{Eh2J8Ksn8!yrRf1C)1BZq8W1*`6zOF&j@JBv zWNvAB0Zn}722YkPZ7GfU!*T1`7UN>me>p!Y_in834_@(O)>(ZaW(HNUh<%hAJ!!Y6 zL_k|MxP+xk8kG-;c(dkO7+@k0TI2s_b)bcvuw+KN#!=K0YttXPZ_5Gcu(byu=~DC` zmGp8>Z+3Gl86ClwqbtU|%4-)Dyqp8P+)sv}hOP})wGA)38hRw+OLj^T!6{}J;LVtq}`J4u^p001GW7mw`)Rx|D%=GaRWL+54n)NsX&>gj7$;?*q2V%g@IzK&G zxTo(i1H%zRY+=cQPYL5bxcx$T0h)U;dX6PY%~F4AAym*~8}k%XOE=@Ftdqu99`_e4 z+G!dzUmhtHiiX(RO1VHH1d+|Pg)YkV1t_z4F+C zu9Pbx$aWd4GW`d~At1%0M&OyuJf=QzgE+4F4u)>pJ;d7*HF5qn8qapK?BGXT)RG{} zv#BH~SQ}{lWV&5$)R(A$< z{u@u|JcA`OwyUWEt>zfNZ5Rvh#TQ;RZT$RJOx&{9ZC6kHz}0Wo{^;WVZJ=;JR+non zYH__!^rPrf^ygURR#QwhTMWE(VL>&HEhj0rJw=gPJRr83T#xjmPQ^UQ&)isb^}MAy z3!-`MMT}PRJU@X~MCpj9Q(JqQD=#g^s&4zc{Rul7g%|Y@wC-WaM1|Y-zWi>)T!Z8N zB9)qkp+EbjaaE^}h2gEUk++g7IGcOEehS57thBC^{eMQ+7W@RTbEh{Fm>SpeN&Syzvk8@zmIBjYK(j;&i)uZ zC;C=;GIP~Th8|QIos{wglbtTQK7Z96Fl^28i_sTMOjg;U7ilPU9kIWEE4s2v^n8n^ zxAd;q3s$Nnr3C?VGKVOAu<$XIel|!hS#R&U-(`^5c`Xur%p5>!I%OBESl8%X-1ddf zjEArn=hdbdZs@gFG0kz@nt^~g4W}tQ9BNCkH-h8JVn@a1*1@uEfaGQcXH19Y*r{21 zNx3{vgBo23eA%3{KNT)d((_4&8pH~6Xq!itdMkB#UNc5Qt&lUXTE?L|6SByqo*H|5 z_tD?1B67452V##2rVV(%UQb?11iam98kp9(7h|pm8$aDjr_RvG^m5TxRALmi-CvoC z1(SZwZw`8X%RB8+4m4lgRh(W`=3;9=%|rDxIBTj>ym+OcVsy8t{v8_NMy%&dMo7hs zAuQQ2!eLl5FH{cg({Zs;Cd~4tp}S-%st%y7Yc#jGDK(1s^D=C=UG&YE-`J9BK`8#$uJ7bR6yL1z{X_ zy;k0?ym*L;+FF8;2> zU*6a0g%*zd1x_Gt$g;ix3k#cJ3;_J|o*=AS7fig^;~~8i(wHL6IU`5VGk{A-)=!&c zmhjLnt4xt~5EkCA4J--=7dc>&Jub+B(1^ht_b@F4T4ZEbp9vVM{(A7JzvuT(>k+FJa`z?`$=e-r16p7ekU@^jW|BMoOk>1^5FWoITMBXwf z56!U|BXb&}*`#K#Q`4in*fob2$>)QvDPx`7(+;!uD-tMM8X62@{LQ*Wx#S+m_4;`B zz+Ai?JC#{Hj};T?qMD&{Q7p~m>tnPnS%Y+*<8x)KAgB3jL+UO_T+h255>=JnR zr>2sa&ZFo0e_TZ}ES(DWe=Wn*efmHe5!=~DklJ(4hK%X#)5}#zIT0&vj3C)q7WqiH zsn7XL$5d>S>@Htr!A8a#y~(ZMe%;rF2plqNs0o&v&f-eA!kghyk1w!xdB7KVav}jO zZ2pV3 zvs>tBg32<+&9(R5beTm=v;^Ut>!|%t=ArM+D|6Lv-^lR(K zmO}7qsEkvo$ST=MGQ&LY2ky~~T1~UU z(lX13S`F=CYN3+rDh5J4tm$JoPxa7^IE>5gm1^B+x;ENlioC)_fKN&-QlW6YW_{oC zjBch3v9W5By%bdju;{p_52En?GNw*?mmwZ3mHL>1U$JcXaEuzSn9&3!Btt)iqBYeJ z;MREpPX0FF0i(St(R}b|h{3qeIrkr&h++eNT(P2A>zw>nM*Snvuy&M`ZUHE9MEPuH_nWS#}}eBk1~O1Z77cWqrdD3clV{k!rGEAs}cvK-1?q!EYAbZ`{EW zCt?j1@G@}iR%DeJkEq@x(?=L;C8|vGa@pGYK#IJ9N$VsPax4oGKwhQxSssnLOk0lt zS4DM$xO7H_VrR$gjT-eO)gDzqDVE6e8G4AiauWVl>23spu(maUz5@h)MvNpY0&*1j zFIUP;qj6QHTi*CpI&Aw{T?ce)1JUNgsf^8u2g;Sz#jjGIh@8#}2bYuc=!)t{&4|-* zJSyPpWBD{M^GDd%V`MfgtTUT+2)?5Qxhoe#Hzj>MJXu|w7uk%mj=l9hD=XEVKN(7O zNooYf^5-`N;cfBlhHj_6n-$LGo+6v*XC{;UW@kyd9@ntd3dpkv75Q;1H~sg`>&8Y? zQ+Xd}$BG|jOC;ZhaJWRuKV69pNLobbAgH!__N__BD)u&~cna$3z8G)1p{zrwBkAl} zwj`#+03EbiF4!kCEbyk(h*iDpSh*zCnnI?YCJlf~UyP8x?5dnQ;Sau`tO{y_-5Rf$ z^5V`Ra^UC1$5L}CYDUI7w5&{Iih~JzKNH^C_O;rdeJd>?-7gp&@7Fd*tnv~WV|yUs zk}Z+*4@wo6ffF+ae&4X>9(y;@qo#)v#hFwcf1Ik`T;t%@$rdvvgEXs9jc{={Kq|5o zUw2jtGl|?q$PWTfGELpsQ;MVNmV;wZhH=q4Vz(q@F$Ew~%jVx|w~_2hBN~)C@%9DNbQV zUX~&tb}fw&%ggLJtA=_p9OLQPY4Lr(@Isq;ajfN$GydxT-~hIdd_O zcUHP$!Zpo`8MeloSqc<)Tn3oB-uz{m$uy5Xnpsl@;`Vf|4xWR8Y8Ho&mK7se1D*Ui z_(dj&bW_H@S6ZuAPo7o1WHm4`sDeQU()J*>#?V6OrdJAI`}y$Qa#$ILrtQ>VE^TT$ zjg0d^><`JiKLm?8X&1u3y~9>8ZJE3wg-Oeoz63zD_}a`{0b@wqoA^v8$MKcV)c8@N zCUC6zn2Wc;$_kLyWRlvRzl-~}l*^F+iyX%)8(ntGBlhLpliLBqsJd6vvE1W>=0;=1 zi&B&>Dqx9EV`Qau3S;trg^8qliD2d($KcL$3B}g3-KGmPzrVInxul5EvQ;&c4}LO? zPSyM8lSuv!?Nb_t2jhSHk5-HU1u|W#{{r{h{};pkuW{G? zkFNjV-=)YX{5u`d|AS)FUh~w$9TH5;i&EWSrRX2=G~j89`;V-l{1uild=0~C*X|Xj znPJThDL$s|35}jk)~s2H5fT3u@C+#ag(?T)c1j$_d=$=BvrTd&t4emhfG2v%XO}h5 z9Ze5w>vEiua-7nOe)0e_67I~>v`&RUoe;}vRI1{?T@~j>eF+M<$KW;&AdpX`QMAt5 z-Z{4fs{4}r+v#a;>g;2EJWf5Xg#*>E`nMyo|G`8wkcjD{XMRLOU*Zxs06;HYA1QQG zMKJGhy__ukSA>YgWX#^{+=tifPi{F{Z!>}-xn`9bCxqz*OtbkbO%4`K zmaYU$`xR&K4rw3atsN4rn9*!FRAh6+50E08E~94ocItLN{y>|7#cBo6}-L^qE)gT2HWeJVfQp+l8_5h_Dr+dt*+7J2mMP?n=BuvPj)Qlwp2R_G$EL!YpxMhF3g)`a;@KMGUpJ&EuNlQ>)$}ngebB=3 z!`lj^=}qvykvrx2h{X4zeO{@{|3-(3^vYT~+D%AbRcfHS6wsH1W!X`HNOw)_8r>>; zR--8ZWT1*n^Qjl-ry9-pc#7kBRzuB6p1q@P=5CDNTH0|J9T@F?Z(8(uZBpYN74W9k zw|UtMM=RKB#`1Q*2c2XF(jK$lvi#arTMsC{QPWBo^2)5W+px%$?TJq7vC2K|VI!}w zrn>2I4S`&8ime}qeq(E_M`_i)Gx5tyTYJ#Qd+I-wBOMARi74f9Ulmt8#z+_UN@(NHESid2Ts2y(d&lV(2!>$^ zAA14NPKYMefyoAX6~C6CTXXFq0yy45fHEx#G)HB^cVQ0q95vJXPUj2r%S!GkMZl3d zkryx9rQ!s$h`v`uyeWg3EnBZgSC~!n%A$l#hek33$E6bXeoy+0F0SPwK;8vdNABY8 zVBM6d9%5w{2MVWkC6c_WA0h_}TfQ>{WLM1S6G|@gFxcuLy+aOXeSL@!P)#dzl$bm^ zizdl#d130x3dcC@MCGH=aeS0+!Dag6R1?M00Jus})wburW6IZmQO!O!S5C(eE+dj+ zj0Y}MH72=pTze0M?!Mtj(2`2kRS}w?wvCT$`5N39J|N|rF?|&F?RRI+tJX>k<-@$j<444KO5(kdEfR;mCISO^m{qrq!JIOI?MORFem& ztegJ;hk1@}Q$MuFujZ56*0J!ur#J2KRctKTRx8l}Kjt@Ag1tWW?H4(;uK)7$wYPKg zEf5fsA8Sp7A+jaSt3CDu$%sIL)NAT$89_eGHMAK^#3wK8KPRL21m~uX9BJ4#zX|=` zL%iPA;`SrWMVccz-dadnFOYmB%;k@}&N&+z*VHQj#C+J$xR7SVHnX+k{@QDR0Dx8o zz2fzbpJe22IXu}w5I`+eG9$Fm0vmveVm(;Lh{0&DFvAYt;O$gOG`ud{nY$(0J@Vnp z336VlSihG)v6Fcsb(p!ag58#)2JxQN{j zb2hcjrasmInbax{^Qv*gKb~S&&hWS)2i7AV!(fe=^tUhWS^1pjNTWWRqHPh-#LIm2 zPM*Aj1Vfa@hr5Ih*v{CDbdu6oHH}u&9xpWY$qyy3N3}RCW4%XhS#2zD>G5(eVofmE zLzk);dn-|zD>ARCS~ON#6`Trh7(%Ebp4l}CsBQ@ag6j7B$EZHw{CPB!Uwio(@;V!C z3YJhhN22vR2kbe=FnnM6xSi&R8es|IPvm~CHVkqGR^e80+$9S(RbO^scWeC2CDoHt zy~#@d^@a&}vvX~Yas}MQ_35LBI5@D02D_WSD)UYDe{H!)P_OK>HLAW9Np7OPUq*9V zapv2Ti)IeB$U0Gq`uxS{QAY4Vcs;?-6Fw#NnH1Eq$wK$6NnYDHI~Zah8HAE2*fJ#T z>6B6!uZwzmw}5-IXw`BM(X;Xg`?hO7qc4B23YiOyyGK&gWKd3YsQDGOo_f94r@ROr z+R%!3xeaPwZfLQN$qjwOfp{|+E$on?$RB~!xEUicTJpvkxWy{e*H3X5`PfV+-Z|}f zSrXCXPTeghTLWhxEJryZdE2Kx1Jr&CGYYl@uA6v-e*~+^cpuTt@+&Y57(ce8%4{P( z7Vw6y-tlk;F4zV+oO|(AgbL_?x=Q}WM9zWemj=ko9f#(w+(esDxi{BhtBYLg9BSUw zeU9ggs;bo?9f1A?vt%-issMWeyJ4mGV4p5FI8}g~a<9%;ZP>rQ31kv*W0V3ZAUHW= zB8iR_O1-LP_v{G`>J;ZrOPAovgp75KazZa04UofvrSrgM2 zRO+t>^xZYqP*b}xh%Ze_c1qcNx@@DTknu*Oj)+Q@V+>*c+h1$utFJqa!u~;|tu>hL zIf!75Q<8(Jnd2v0T4KL6I1anuwXT%*N;4S*jvt$|AHU8k?*pH+H3x~WEwyj-jb81i z3{(VnbnISDXqzfZHPK*sp8shM@c$~N#{cN={CA+C&8Qa%E|fD7W+CghtR9gx- zzExdtE&QeV`Ls2Nu2Efv@$~h=!j%BkVHOpo(;N7IKo`PNpTN-K2CVy+}P^N?zfOxcI$$ zGTVOzvG)I5^FJmm{|^w4&ExYgBX3;C-j4sv<7=0O;MV0XnS`UiKE=)lk7S7Voe}%e zzPX*+6TAmt~dgK!n; zTV7qybcbj!e?K4o+dZP24$nK)iOls_egXe#0S^ensX`OQ?D#i!_n5cM(-C1G`Zbr+ zaq~7()>N8NlNHWIv9^eVQ|LnSZI65?t>MP*;;{*(A?32{aj;7C#dY>F4x@(`V-k^N zkLhK2xt!lBdtEgv2yFu@MhjHk)<$A@FRQWW^=AV-ofd66^jtS;!kJfS2%#u;YVUa5 zC)J!7>UI*vU=ae%=8UVH?D>7Hb!qYLXzrnS#6LKW7QL;7NrBr$O}$EYXJ%HL!M`7! zjsi@e`ZSoGJ+UFR{`qk!+H&uRrAi1>lxetBa>mXMz+qY4hFK~qV&r}>#d4NznzX?2 zA$U%v2exVa)DQJizD( zYMbnhKUp=TW4fDy4cX{P7%DV2U#t&-931elB{&PLr$J&<7Hs>($MA?+16YPws7^(l zFRlBH&KJyD#~%HIBkguQ4jc>lyTsltyMSI=P9;FaL|fI%&d<1xV8-6WTSrqhOMX%r zvh1Tm{3f`hRJ@F~&a+rRSALmr?9*(Gm@sAyUn3Uk)H|hKaKRh&qIkT|gm;?vxl~tK zEx<5evN-aqjM(+{xzy^cTzl(;GfL9iYkn@Rr@J}H$8Q0@V;xgFQ4$~#0GLKL-w=RT z+N1L(k!nB^Mx;@G8YV-@R`8`%=LsJx;ZB(}&bBVro1Z}Kn7ZY`U4p1Bf)F{W3Jsii z*$8PQ-g``pEK>cmQCIpI^C4Lr%yya9MO%>>zOJX6?J3FQ796!HTjjfT%UGyU!i^c+ zI?qU=*lEU0Rmlv1o^C9h%3PjkO87+HnlY}NFgCOj$qf!}3LDyuA9DZA1R5yAEMZ)6 zJt7#%C6Pm_^B1UXiQ-SlNCFenbJT)0;g_cF41Y?~Uqj9INeSgPec3Ic{j?!6zt!rG z>%*kTi~H=I^E*5$%({2&Y&pv)=za(^|BzZMA@4aWyfX9TKOf)n$dKo!w<0E$mG!sG%yWAo;_nc*u4c;-W6bI#(lmf)nGHpZbQV9wKuXb2Mvwga*KHqjeMrN5|`@P16DZ|dv$Zl@|b zSDWBCQH}u?p8*!~7#C4?ktC%@H6=E zLd<8H>zlp|Z6H#HZZc!6?*vuHsK&|l503Zm=GPeOGx5%p&DYKJqCcB7Gth<$w?g$9 zFVM0J#j}<0Y|>$eAqcC)ciM0DE6w*K%>~sGrEgA1FegR<*&$2r4)4-|X%dF_Tng}K<3KgNNBzeGwDQa(2)YRUq?qr){VWr$l=3L&7ybt0 z5a(NdXKSuh&#xver~UD_yEpyDtV5e6FD5?eooyS*2($(391HIw+QRLfWfzvD76b)V zT)g6=1e?}xaB$(aG>a1vCoE{cVT_{>7t|W{WmV0mAk-7^csQ!`75%$^u`T{)#y(jX z!1lz%!W-e1h34BI@YC8x%DB5X!DTIm&o}Cx6C{3>Po^VG%3(Qvk`zSf&&J~IrWS9L za`6(GY7`RueT_6YW%rcMPftqWO3zBQ+{d;16sO}8OWryx0x*u0eTFu^R8pQUn*7^p z9-sMmQ){vB>skpdKl#x2~o$VVIw4eEJ z(uuK1)Gs7{c@-V#T;knaHZf}s`e;%4q;BAgKm%)@d=6i8==A#C4iIc!trdDt8Fy)| zmLbyAqEPPx9egz`>veHcFWOApr#ic^lgev(1JuDtbxAhvURp&Q`jh5uqsPK*!B~XQ z=$OTv%c@Ji)z9T9c`&Wrhbn$({iWHT2OT@!OD5BMD79vLRL0Lf0PlPyty|w0#ThYb zmOL?f3A_m`oGt{WN#!KPCYboT)w4W`9iA!v)*@PRsil2GSnhldPL^X&ZM5L@rHfI} zojfS0ZH@sUnA^i$N1VlN75@-rf(+K`0F}p~!NqfzHyX7_q^YUew>`LnjuL0l`=~bj zwm*bgt#90xoPFsQH=&DObz>|Ag7<5`MXbN?!FhYdWv zFAE~{3rd5UxVq**1&N$r4MSP&1zVzR%q@Q4Xa1W;IbK$SaDEOjrH;ql-wX1;9lE@XhT;#2K-zg^1K+25lRb zMz+s?$rFn}5{jiOv6L1I^BTl8F|&I)9zVUJkR3*tcYxrSG_ev7)}l$l&5PExnA@?b zkK^WfD^*V&GLo_9zI7AwHGyX5iRnnROHg z;8OpX>W)9?E zO;IUN(S16m>&?|)?D1%xsSpu+@(6gwr?7Ck^#Zb{_S_LlKi$9v89Jcag`IE*3US*w zHPZK{8ziKsfrkmril(0=^tj%eHNG$FHKDTwZ?#cQdF=X?XNncva92t#q#rQXhYI*n zvvM|`;|eD{pmSXkTMCtF5Z<1t&v@w1oEVs1DkQt04fsCA^1}9nkct#KXL0TqQa{rI$g807sLQBYT~MCqE=;~4 z3444MbKm-{A_rhsFc%AfkFT@j1i{G<(K6M=%a`c254AZg<=|bIwHx>(CB!;HV-NS} z@D2W>#nejCpKUL~^fDDuQ-ASUkyDxW3s`)!7Fbiw$rj4XXYr%(}UF2sy(AsKWUy$`gd$rO`lIQKKMe>6Fwh5_r41h4rf8ejP5titydoe zfRG>lO_@cQxrz?~bV{^Fy?Hg|LCqXt%%}gPEPMEKuuTf2IZU0Y(lIEamq#W5q!NNB|FP6<6NuHbEnhX&n_C~x@U2{&;JdQ3KHVi%`HqM}%(cTroI)f`s zPS}9fq5zUitZ_2XY&VT*q~8d|mZ^mzQ}%@T!$Sj3diht9zBUYD8K%*%4`wy1+cKn6 z>YGQJp@ygAgj5iV%qq7YTZhBNNTtKq#|(|4;i#K^leP7f9qTvIp;D-UP9n?6qYBg? z$N>(1uGvPF$+l=}hV4uNolh;wdo2ez&?SDQNW|M&ubHfD@GF%IK~w`QsNO&FMjxCH zcAlzdc6m|MzRPveu-azI8PuvpAlIO0cE-dHUVP4c^5xG)%SPWS101}nHe#HQ%(Cd5 zaYJhm^HI@zQXSE{mVHXQs<(Xd;Xl)not0a`$o1@ptj7w%@~Bm$gQ|vSC;h>%W6L%C zinmPy;H}f`^Y)W(Gr&G;P&_^#>Vf|LSG^+BxtGQ?*+{h7w#YeJh{kLL1_9g9<)ycs zHpXWc+Tl@dSn*Gi4`2nEjdu=}3g5~p3uEfvWhG~6wrgJ|V^cYHOt^DW;oH8@JZ3dk z3Vwm?otrLzP}E!TUV)*$ezH>?m1C_B8Fno+fjLdG78-0^_##ft$;*~n?$7%A%bys7f~L#l(h-Je`WBHUcN*!A##&S5 zxo4;Kn5%H^!Ne5S1kKg12nYqTteW&ZunVH-0L2$HBwf0|hY@|1`tS>n!@!RPvX z5z1qPqsS-LyPv12;hKfzT=X&iB3kFbhAUVzel6)sU6BZ2@0)G1%PmGnO41IvZ!@Uj zmw%gIN7;%0d5YKNsl15zqRFrN`(+iB*Q4~HqJkIU;nOw_vleLhip>eB_$sh}(++rX zuu+3_CfG4&3cWX_bw;vQpQ%wo#e`opQ_M$j0S z0Q;fb1?v~bAvJcL&`C= zV>X7j5GWxnfgeEgTdRT6^Sn;=M(Q7&tF2x|&-fqZpSnL$Mi+Rvo%H@1+#0G*N(O8x zIog`QHOiB*$m~=g8*q6W-IF9?mtJhTvo1Z446kqbq7cVt=c>eXoSkO3CcNmV@*OP0 zR@>MTCfANF+~)UtdduBPo6OTDUeaDX=dE9N?&YY!C<-jR19AsUfO|eW zvr|-zII&uis$4|ae+f8_s4v`!-$rnb70vI&tHdrY`vfzrOei>aNe0xE8&4UGiW-Q* z+CrC0S8wjLjH;)|y1;B-dg!w}cfZydjq2faS8paZZ00xy81~jUN+5kvb?KIH$4IyH zE;rD|mx(_uf=pfkLj1m7pogMlC2{J z)}vy2PgiY+$9M6cMi#VR#T{Jk?7 zr8Zw(a+X^6rcNaarVBrb`yQwE;=D320 zq*am5v=C`k3Z~>y&gH~ol2iE;>*~q9kM&{NKplYR@=j%G?GWa)o-Qk=y$9WB!ja7B zik*WSQ+Ds0Fz)4-LKx58QwedCOh=99kflsku7&gkw8~>9V~O2A;M3V7Md8JuREk=7 zXKT4xTo0kk`-GacoGzKOXfiDr7p#kR2#{S?UCPxri9b_B1DVQ5Jr9Z-@U=PdPwnJ$ z^-O-%^in~+bn@F>wpans-`=-=nYr6|n}~u6$9jp4QjZb~_1DdfR;Q|D9IRP?>BY-}LOIH!taAAj`NN$xo7HH$ zGyKGy$Hl>ZUv{GW0{V1&px|Xmi0}Qx)nACn=*h3HIzP{>+jtH=9R`EP9|`9#DlH9R zeK-k}IMDUKeT@i&VaIh*m?L|;nx8pK+=7bV^9Fs}{?}~QOrw6EwYA^rT!gSut1Yw3 zh_u~L9h3iqlL(#Mg=r9UlDS0GNYp|eUnAT$OZ!wOrliD6Kqi{mrAwwho)TCC%G&m{ zVsH6SD!QWpN2Q}2I76t%r<92~+!&Dr$$ne@+PkIWatfVy7dSD0*Eqq;vD&8!;Kf*_ z<0rl0&SeW7WV{RokaOdfX{X0ri1@?g(iX|p$XKE-KO9v36vTPWXtS<` zWwDDik@PMVUDWC2S$^e*sIUQQ`e1*(XZ}oXCTI zDgT~w3Puv3$)(3%4`O)vQlG!w!p*ezbaLP!6{cxMgB&ke>@Gn%OEVF8jLcy=Eo_?* z`n|6buM?nR9VDUd1=#Sh$tm0$lXjG5@2M)r#$!>Mc;u893;%33a(-VhluC;aP-6bU z_5B5DjD$Y7d{ZOyZPj2OFX)4;;Di>bn{R#ArCl=v%Qzq(=g zhAosib~C3^V_e@E)oBJxGe%bdc_7mG9}hU)ISs6RGg^YZ=e z_N(!~H_hqftKi6BnN!-z8DJ?V2d&B`G+?JCkTU^2X^ccxBWsY;)-lvfVZ)X*1?9oC zl8M=af#)mR5j~d3_}QZ#Hm%yoDRE}^Yp-qsH4PxKWz1qlN4xL*yP%h zf)$SSm%~;a&KBIisP!bMe2NoPYW$4*SIVF#wg<34jf_l$gS<>aQ}Gti%Ro7nrtT=! zSK3VS^g1r&%d3_* zziSFO2k%T2hvFNu68n4yOZO@&g#jU7CYHhJX@XoIdPmoZTHgMpAJGH#${cG)S;<}H zBFMB&QvP>qBL4Nf|8XOsbd1OGok;s+WgOBX4L91YHz62>gI^q6`%?=H@M=jYcmxy_ zQNHqxH+L>IRx8?}G)Vk7;tY6yqp65trE`&tRb8k)M)e?C;}Uv%4|aZ9&75GUhHwTr zpU&WQsPr!r3ii024qphW2vv-+Mgh}fu(qO!hx)ue`PS9+WZF6(3>E%x(-45Wf8kFp z6`8OAe={tr2T4Aq{t&?_qEr>K+5ITpJyeVk{@gsx0J zFrd?*DvqV^nf8;(E)<2B>pi#%AqB1a)3hhW;cTHoVcf7v5(rxKIw5 z5s*DBTcea>xMa+MnV7T8zCIRd4=6YDPgqj7<^R!_(kLdkClN@V(n8Ss^MTuL*31-1 z@c*sDPV%v~@f*CAob-sjRQkKGeQ@97Oy|jGIzRklWuzE=U}vR2Ky7RO@bB))RLV7` zy8YOuW}CvWo_odAuQ@W@CUdy)HLW$KZXP!3)){=L_x%_(eU==uktrH_z-<>IGndgb zAt#-bYi|nbUD_G0VSY&cNCf{$$B55JY`3)PY$PhyY%j|fKp z0skqq6YmCZu0oo%fIfE1MfWLIo96EAirbxR zCiPeMMgg;bvSNO$MHw81-r^VdD^~xi+TS;n0|{FfoA-dY65bzw%dq71iP~>?Vk;T> z&@=7{EW3EN@?bbk3TQzD}ATu^uA?ZDGg*1{UlqXxQEJ{ z$pC4Lq2Ez**E36h`XZ7vU`98~QgW->-1N(Xn_>)`)7UEdA)YOPmMUHIEMO%$Ly?JY zjoNl?9nYtf^|H7`L(rT+RtxB=EHj8+gsGh87do*%Vo%*5d*I4K(8nkO_E~i~#k4~g z3hRIs!`AY|X@I)3tC53!ln!@<`Q$xLar61vEkNxjemqhJt7Rh`Z`AJVF> zLnS&PCajIsz15B8+XWxwzg|YW&BG z{GYt+8j~f)J(HrW@xtfNEz+a^m`Y0;P29>kd%?S;dva1?egtbO0>hOdA}^>P5+?)& z%bekM`UiCm9L*3aEfj1X%bZzs$p~W2s~tx-o(TaZ4%A9mQ7L}GE;b45JB3_jI8-}0 zGJkUzj}yGTOzx+V<3gL+w#JAArAFrYP#EU^G>`QkoC=eCc^U2HMQ zrHBa?YO`=kr_p}-H@MtiJdcg}yeA-{=J!+BUd8FTL5!bROJ99Mbo&hJ7h~gZmiIFc zKS$)1JIcLPcwdx}L+nzf;QAZoq)a!BP zsWbfD$Zv2_N>bK$Lh)~3X$RXp>Ud1ln(Ul}$ad?vRtjD}R@g-cfjX>9i?Z#(9)P_k zUDfs+^7JL&ZIv%7MK-%ZQ7;(KM%gOQL1wNEgm|vDkEHOSGAa5}!LDM%< z9FyqF_=r6T7h_n@bu$Z%bSB@Zwti9G_J)T=(JVKOL_naG6ML?-00Y)&o5c^m;`&BMss}ize>593?bn za{U-G;UlGU3z2$l4dFvAoP5M>A6sRBJIz0Fpnc?DOQ)iwM+TB?;o&L2-~GCMp66J* z6<)d{TJOc*G$z!^%Ojl?7lnu;dd$ZtXv3IN-S*1CvyJk&t_nWy^JNt6q_iQj`n~c` zl>c_Dn<)=FMqe*>gpmbr-xto2cW1CwDERRaTn0wc01*l#he+svp!x; z1Zbwx>@7gfn!Jbe_hD`Oib(5?*SAV_k)E?2?}J3}-iMRtgp0Y_2H3`kzOQ&oU|aSm zX4}m~bDTCbZYeA~?xQS8Y#P-1TUq|P+DQ**lFXiO zAT(;t^y$}1z3xZ7^jJL6^b;=q7S!+#Olues94VM zZxC@0yo)OH{UnNo^s0KmW8I3heGK&VGt*dWZlvV)i%)$v*6qRmT}fNyz|EmJ*1 zK8>`?JU=E)JKAWcE0A;ULzx>*#xvBmHjt>Yu#3vum1bg{Ev&Urh~a9Ki_DVk>@K&? zw3K^BQLr9K(yP><4Gl6QU|A!QMfg2EQ&P#!RrbI#ZQZPQ%NMXcZ% z{zo-ZK|ww`5*!@jQ$xwM5nj!4e>JzKsS=BGR^kOGn=yZsn)u8GnMtz?*O;=_5TY8# z?kN(IATQs5l&WH%-WBeH?#A$leLKY%1*jt7NH=|BkJ%9vVQ;?{DQyMf7NP}tq!Gijel8*xNd_g{xnPrfo|*h( z_6U@;!83viLoZ&=z9o4QIcK;3tG%}hi}UN+JV}Ck2(H1kaMuu^umAyq7Vc0KP`Hzj z;BG~5r*JLYlHl%c!GmjX_;^>BZwe_|JX8KcXRO*g@2vcqC02gMNCtAeRrszc&kGc50 zHU?COJqpp*{?1}Y$RGx*41PGBur$XDLKunh$Z=n0Rs{z9PBB6eLErwXK~q(lw`{N2 zeXrBe(0J)M(J=PMU~Cg;!CJBqR!Ir#e`q=tpOvT%oN`pAdD;C{U}u$4FP6ETB4+VJamBWPUm zRpF$hMA2e(^od6Yw)e84(sq;v%(aq=V$#1!^hNT?_IZkVu=M$^^E2qv1sQW$$PJ})e!}J#K zvi@@acgvVi8qdz(UMCi+e1J@96b|I15y+H7s51h-PgIz%fzTKF=|u2BvWhRqfl;OW zL3U94u8oRTP@E;I%)t;37T#Rv)hM1^Kg4Y?8*Yysib)vny`1ek_cR{PWqGSvIJ8DJ z%?zAn)nFR*K!~K0jqve`Nr`X8el2os-HRzaM@d&*W&A*ySyC#TUpu%v_+_R#ttM$E zhewDuX>)%1N?9E2GD)aYv=5c(dH}$A!>MWrLt}l|2RxdhXtq97MY;}+w6utz z?=TDH)jkd>?S3!uFl6Jf=Jzz>iBp{n$Gdj1FwNj)-JV-p>VV_M&5H(TK=@n5P;pNwYmfWba|X)K}qUMMO` zuY>EYUJ@7{w99UV3)=a!BkhF6MhB$n@^4KCJ9Zl4ebPP#oAS5}$nbC5MjE9s6ky&x z%d-thmJg=UzCEjqjr#o%5$w}c1HRnf{as6yAItj#@<-b7Lab`MTe#ILw3A1nc*$P4 zPr#-g%L?YkV*X|69TKYOB&y zyEP;D4l|09n7|7@=51)V+g7t&H{VrUMfe_F$|`mnROHKJS?X|HTHP&a@O8&~EDV#pcXE zBaK0C*tA1CaGD0Zt~!l#W|7JIf^_6@ua_EX`zP$OXzfHKmD}B<111_fqh%!uUD4Jm zK8c6E_q-aAA`on%R9mgevJ4RP12}tSc&=3Ma!~Uwb%~hlMNYIfcOqlvqA5Q(cg>uf zp*)l~Kj!-3bFBBM8RD$JmTHJ#RG(&4lb%co+Q~F!59*C-dc>~LN||EJ0FlF5)pmB% zJWBd$9gj^!_flP5#sW3J4xd#{7ip0!dAB{YBij;xOsG&4`Am;hZ|4z-ajWz<`UC%< zbe2aOo%#I^^yvkw4HX!57>AJ+E!O8Fb1=_I+K`q=@+Kl5PNCTu-O2Xv1M)r;BpFXB zT-{Y-Wvsv%)cKl#$gQzD+#83tkh8K(Qu9X%4U*%ivm3QJ^e@E+abDM)c3XA9Yh%`7 z_z>xNHvyt5rD2XUGPo(MuxTC;gADyZy8v&SWmbHJU8IqU zPDa%?qh>ZzLnBwu*1xzrhtG+VJav@)0kBlKA1+ifCn{z0z4^eb!I#y9QUQmWsZXI) zi<48P2!3mt^<`}JZgsicIy_vsj50=lzESR+Io?>ewXh^3I?>!Yb>zWFf{m->UF~00 z>rR~>2~~$a3x}ys0^F4XCoLi^VfHl1BkwM827bg#l$Bh)D$=0)_9(>DV{?Xat+Wb5 zh)=7rw>&7l-)Y=dtRVzWrEz{uUn<5(dH-pr+%zJZuXV6FTR}l=-m-2_RCLVk{-$dF zr=dw4>sNoB3I|`G8Jp?!2@rgV@^z}0>Gy3bTW6Hg8#?dX>D2m|xPN(kUn_Xs1L8s- zb)bRQ_aFmFN|KqWWeO`oMc2&L-pckj!w`O17mD130<%OkQApUl6u4_fu=3@h z#(D6f-LTyf@nnX*T2Jg{oG5URjUQs>*0zJ)?X0bRTi-+a`j+5lUC=L);<{0^MS1?! z*ZADzcwJo>tkt7AMH+M5^KH!>nNv;dzy3T^&9uwnJvIuj+pFSuwzNn)^w$ZAjgJm) zUy-EUp4{{s9zEh%7avmEh6AdQYS9VTRa?{o4{AM@&P4GKjPHceNskmaRbMxPz9DGb z6s8Mj5SLz1p9&=AGa}udkjVz`VT>BLS~K2v+B+6_*B+6gZamJF-w{j~)=FE`D)iJ5 zjzB!%J*|!wnE065JxKO4>cSN1ah%llieK@EjB&!wZ6b=hH=>Lc7^kjQHJwI?4D1cl zSD6x029hBui1>;F~zi~nbsJ_13p798kg%$S^ZqlVM zc2V4o3jqZ=%K4atN43paR%pfsPei&2vV`xJn$hhUFEvu8Pbu`%W^}~i<>SS6F%q_ev_A~_>UkC10>}D) zwF;NrJEc_^9k?oUDwa9y+axGZIbwbt19JB>)efM%?X@>t_|r)_5y$}grDrmgo81F? zA-X8M#s2OjZ1y?z2gs}=I9~h}LhMhX2eTThk zydezJWJpx~T^ITy;&5hOruf!PeMa(ivB@Bntd~dCQ^W~z<967PTuevjv)ie<=(pKX z61$~6Dh1zvP$Jq{i@d?A=dSCwDpGuoSpY=&aGH~xv4EHs(CWqDJF8zW(R?silQM5s z=NgI(g@hD-89kp%dV0Mv9QB}SuZFzPlq{#>S8QpF*=vOMYB=Yw8SghOed9f;|JSaF zl>B>F0zXJ;PQd5_?U1b*7Pw-g3dElU(@x(e+ zxtA0+U2?Uzk?$s0thO4F+E~)7Ee_y{RTP<++_cS%FR9*Xh|r*s90)(Zn)8ANHfZn=GExm0INBbbH?CX> zgMyZLM4>2VS^eHFZa+vW4I}Bu{?4>~s@);h%U3P@(@AxP*9mtkR4hM0!VHCF;mt5M z?A-Tlv)GsOR0e@JVkN0+XUqEDUwh_S{ipUDFS{YO#cL`g(I z=jc&zgIh}#k!*@E+4*XrH&{(R+^C~YDAG2A%#hECdeFmI_5cSNEE)ZX-DD(?WkZrN z*E^?&lGqUECov9}XHzMZajXgDd`a&5lB>JR9{)kaD&!4t|i1SJY5dL^kXv z4c|GyrIKJ)^8eZFM8|O?rZc8t2oG10oD%flsu~C6cLe=>CODhj3%!c)zynk{RUMt4 z*reHd$TV9bxIt7Ia7HJL_dZo3o6>QXh3kJV_OCaGB%WB-?($-R9*XV~we3mKr3Vh? zk84Q&a8v&p$BC1@0Tw8_N`KV)6jSH6ZCmd&h%;BRP;lLmtylhT6E$V z6NMudDLm`4id^wAkNb(2YDerX8`t3G!uI&l-w zV_hM4l-fCBu4TjJ(b;i$nO5%bVsg-o&jV)NoQx&{M zOp$8+RH?N=F{}CAAkdSJpT0(xcnjJW2bSYaap^8Ol{CHIM90-nxh#YX>6N_oN1~S9 z%RRP6hfEt|`_c4}s?ViL?C43}ML~Or6JXgjYclZ%HNJ1TUjFhe5mk(UVdD&fQ;PPW z|AXH^k+Jqjm#zXn>e=0B6Owc|F4W)P;9x>ioAHa5?wl5mah+0}li!WKAKyu{V_(EE zl|r9Lcl{OjX>B&CS3z}^elF4<9zAKink-IJ-MuI&D;z?zTOeG?jNt8<*+h*SiV6NL zYWtH3R~UGrKi66RdzGNOyb|1H4KBxo9PQOlTc!Q7*}x&O)o%axB7FJljVsu+qKMNq z`A{*vANF(vO^Pq;EN&1Q$9JHmN}rPgE+1VOEf;Mwx;Fk;x9~xTHF}FQG zo;GKaTKu3U*d+FI_FaPnbnAF%3}$+$&*{;bKbSWq8>HdN*j+3SU%m`w462$Et=5`r z7uYC17m2!}lA=3;?(6E2eO${(ql7X!P|*cBjnI7MP{&x4a!3L<@;=fxCOk?M57J$a zBs^smwl3&{`b41rZaZX^NpaYlK(<@xBJhCjaO<7hoNT& ze@dy@9c;m2*1GGADNC;&P(&-1CTfvU2c-rLtJ!HjUJfm*lEOy=-w$EbfgV|Ran**- zX2?y9M{^f<4hJBrF%PbJ3&S*LtRmU1etse?^WsOxOEhRaq2Uc~Igq-B9p3nT#-K|p zO}69-PjfwG%saE`a^A=o9(^y%xgKR;Ugtfjq-+Ga#9HHPFo--0wBQtwOCOT588ZH; zbnp8iMiLrgYlWJKF*;r4;7oYy8-#n%uhyQ((jpH`mLHL~p7)%?i0RI|DV%ASaVxZM zH9qeCg`Y>Ifs5gJPVSnq%o)u`5FO-MhwwU4$nP}YlyY@`1HM^m=pO{k^l4M!`hb(Z z(RRZ(48T8@S5U~LTSgr~-hrEtmzM^YL=qDAo1X;I>0foKb#fycNSy?0{OKsKQ`BcImOk>6o_JG%I;jeso7toUiGZ z8Fb->Db*1?LN<|8&>$RxaeG1eA7&m4mOmgvWiKsvSHF=AYnr4u2BQuS z=vG{YP@kr{e(Si;8)_-?r}#0SXGNiM_-*W=#@Taap#rsKH)A8_+pi#Pf7LF&KNCOA;fBlhWL-`TO`&2hIZ@zX|D800FHA|zIh*|? zqx-&)vD#9QfdM?j3(Ca(l;%kok}@n#|G~|^UdHs%*N1dDx7@1cCN9%CIl@X$Sgs-Be2mIw9NqY z7T?9hEZk@1LHdY~0vOwP@?U=e0Oz7OMdOh!EH^xO-PhO(h=plJ@EaiZTyEAmv4`_B z?SIK&X@0Xj3*(Ho4W`;Tjty-C(?#BA=L9$h(B>3nX_4#Bo^FRMDVt-}a0dUdr6&3U z!j!fU!QKR`NWB?3JG!sF4oUQJ$@0wbcybI4+r4i5({iV7_TtFe?-37n>`FnC> zn#X;Z#^TrNlB#>x<%_RgYdYRp>T{Ac`{~B4eSpH{gSqm;g%y1V9_qw};XO1B9?#Jb zCW7eN1C6b`He(aNw3h%YPH2L`$ z0DbOx{0P<>1Ivw~zVw2HzrbeceRJux=Dua|yq?XZ0HJgx?{P8fT#{@qJhcx(7r$)TO z9JSRk&?}9?Jf5PxBkfhN$lg-@4xGY(uz-AMQtWh-+H$FbrTI9>tK2P1c$D=HS{w#^xrGK}O5c)_k^F6+Q`Ugf;QF z$G+e{G0P2>v!ny?ggvTBf5AJhUaYJ>;vpgRTeyFJ zZ`|jYH`##ENuT7q$f6BWSD9(JJ$j&(-Vje$=6Mb_YF z#@2KC8WAk)fY?5OG)s&Ty3)^|Oy_Or^)}IpocRMuXcnjm!6Y3&-VzctkMd{Q%L=je zl>FkmRWrrCg`NKL9T87SEEpv;-x+FA)Fn3qD(+$m!DV`I;*P!x4oh)hEn(arFg=G=HZtkQ!@0nv!Gr=TjGaGjDwJ2y1Zqp8$L z)KAO-ND87ML&MQ2yGa2TX2#XU54E`-U50Im|3RkYxrD36_}k}^7kaEMkJMvgB`KWj-P2PGdlJ+rK> zfDZW-i|t{SdNHuO%3POeJ`ZQ!oeU@JEfhzpjIVRer_-0u2u(v^rsY`*`NbovVv+ac z)ha#>_m`UP|AWfO3l?X8bii5NCM72{GC(wBQ(c0;*c-!AgT;j9vI%J913 zg;v@3hg^se{_Lox>f$>w20J_Pzk8v7?S!aEu_Waax^OEB&6D?{=>h{dW^cmHvhw_- z-l$JGF{D_NkwPS>zy9F3AjD|ISpCCE8W}jo??v^^%5&OZt0QqwC(AvCVd+Nu%ke2U zj2%b0-7MSsjy&7-i|kDqOy+fi1kNgpcYY(grR7s5WKkoiQ>YU zoL}%Cdy=W~9&yf+1wNbAyjtbI-)LC0F_yMx!NDdcd|8rMo{*Ee$0b@J`b_Vj3Z@NOW;H&D> zpKs`5X8I!ERcYP2ydK^`b^R5#f^L;6kLJ|q)DY=o+&X-~ocW$Rf zqeucG<95Wf+Sc)V4Z_3pZdp6-zjoG5SuZ^L)nlYnpzy_md&m09 zg{BUob2f6WVlk30oL`2KBj7*sQ|ls(YK0KTo@+nUX!;`H>+)mPI<1$o;e;=i&WEDk zp#*F~Z<;NX!+I!p_9BI%vQ+MpSBf^*cP zw>b(lLwZcgSqpue_U5GgMB?-IdFD+e4jnaEn0HAIZ-!|p8l)_Q&>F@Ze9IJFlx&sL zs$i}xu%a~8hKSv+mq=7*O;nar`?NDMiHxV~n^YiSM)NSm7i_L>m*?T04$LW%zYiI3 z-u(1X`VY!WNWpoPZlmL)@--cfgSb5DgLPLnJ8Sc$ct5#C?a}gTo!j%!oCk3ct$y05 z=_;M2v)|S~DCA5wx(|V+?gl%OAzWSpTTdy2j~vc);9Bq+Ym1)(798}#-s<~?c{rMB zKZSE&xwmak&{_0g5SNQ{TQeeY4(oBWQx+QL?L)H(y_Q+sPHJ9l1P z%wwXS-dhoZ#ZyzQ@mWULwDf+~nS?Sp6JiwF_ne(mYTKuUz>=XOhZ0$|A za_*K*(^-{&LEgfH#mnM%He}nolBEBj6o>8p-EH68^lEK}*<#ZIxqpfFfD>I^a#0Ev zTsSr4&VfZ6%YNYi>!_m}dg)0B6hb_qjMyfPeK(x#&6qyYoOM#gl>RPn+v4c;OK#=C zU!PybnIQ+D;YZ6Y?)wiyHXb=x4a!`S0eO8<4!V8|3~p}D#2Ic3TlOA9No`GrUGT$H zLwGNMmg09q4E;OVwy1`4ZN%!Ox|od}J9IFhTSt(Vq)GTHbJs&mz=wCyC-MGMoai$YeD`DDnSNbcO`avpwP6QO? zebYK$$=mpKp3%rb)c~BHL~qZf%{}#*zokO@ztaR&#Q=#QU1Y#+DANjN-={aBgAWb8 zP0>PK$J=vV2o^Opf(LlmjhBaDw z^h?=QqC8R7Vi044D+dW#$LPdv3cG@g_rar%pKakEY4(O}9=u`6`6%?Hr&MQK!C}(N zY)xg;Byw)~v@y=?SgRRnVh5yu-geL6K)6N@>THkeT1LLj>Ry(Y!K_!xbU&9}w24Re zAYuwpxlx#BYDIp$#C78V#gXrqzg5$R^5>%U;qXbc&GR^)>r73IRts!5lH) z#1qz|W4dqEDat|G(}L@`LB9s<+@r*{YdafbN1Y1vNS7CX{`fhr{%z~z+{6-EX=-fW zDDK}xbzG4y=hQ|Fu@$q7Z#fWmFV|b+oBd00>@0m4bX}M*>f5Om_;(@^&Im_uN}(`* z9J+SZb^Id7ced0pnuvJa6G6e1@aE#PIMO&~*=v}ZWpSbG>~YNrwZ~pHR1jw}tB0`tOo>=g zVok3t_dYb3cRoJlVQzq3kQp#Gt72Nl*G040d>TY?r(r{SEr;vCr$j|kWJwYMsz>5k zdp$Hl?lICs)2gxdj?cpm5nLKHzqsYQ{NhJ161yh=U{H6lxAdi|i0&G#8om^TZENH` zr+u~BaE}qGA|ChgC>_Q&PV}Y82Y=1d0Jv_>vHWN`2562grvrD*(#q_Z=}R;r=d_c* z`NqSj@7Ud6JQfXtZf7FpqBT2Y=uel2{%1a)IILk(J6#H33{aMnPzzvJUH)r1_VXf1a7T&hCvWGi$@0w8;pKK>d>ZXkt_a zE(84&e@XtRaBCXDKg|nTcv5cZ8!p|d8bK(3Rtu>rOHbOYS@B^gt8o3aWJh$4t+U_N z>YO$Lka;avr{)}>^#b-DN=ZJ8PEo5dLahIl>ww`of@L+Vy zHQZ0ti-k=cT-)q|dgX%4P_^o~#pB6f56CGdQ?0YH)Q$Dm-uEYSLsW&6Fh*;zOHp3j zh)bur)}mQPGcM@7icIaiv$k-5Qh#|RuQ3vsj+Tg-`QwP!kky(+pq~nJkF`{gCswH8 z9Br=Y`UV0#rWD(~D6mdZ>P8QLS!j9dB73O+z>zun{o>PsM-Z-1L7D%%rq-7Fdd1kj zQ(q9MvTOjA-;5tvmK)UiLTdEm9Mf0RTj!^0?i@?74|?^rm9S^+G6C}jpf(t{QTR8h zg}NAPrMco3z)(sZ@SY+U(HzMV5;zPYhL-V~z0YS^*~nnE6O#4F=$*t*uD8?>!SUEW z(tenwa?4)#E!9dYf&T$hdM2jkDT8(gSA0-I$6TbKV+0MpVDOyk8EnaNV-&FUyqtj6 zrtGzILVBLH_2yOc9&Kj8ed zQjy7GEe4b81|TGqrfS-0t097WXBHkkQYHd<-KV2UtAv{L_0YJp1HPY*cGH734XjHz zf*>v-xUd5)GP5t=adC-2*_blD7n|+l3g@ueTtlIvlo{}gWFu7An%GMf45AVb1{N9- zcGSrd?|DPy%&mytsDwilVH-ai9!DQ95;}*mb0B_GVGArw!%uE$kT{gV-|u9bd&Cji z$!f)WKjas+;JBqkw2{aEo1+iK_~*+;6bwl{jG)bjw&8Ez_wUsZ@x!}4HTyMHW6K&R z=WKLW6JmFMIyUFhB{FsWOWSb|0Hu1qkcfg|7!%0>&YMJU8V)sZZ+cF+NI>neewzR8 z1y}ypv7|W-0Pr$qRk`l~S|mEWHsbleDyWcP*k@`_l!MpvcdNg=6#aUx&#lk%n9P&!j%_!XciWQ>LM^zp`>&MTPpZ;hE3mU}=|7X1ADTs1M^Q|hCOjmTDV!^>9*`6$FX!e+QmYSak_kT!*Gizv2_`A|Ym0{_ z3}+#u#Z`^k1{I|JYIa5Zr@Gk4{Bb>k#C|9>X%$(IDKY`?+gI%emi4-)GE+`F6vs8s zEt=X$IIWf?qx64pM{kxu)vG5c)PXjU2c6XRP*d-46XW718I_2?IPvQ}r8nBosLP65 z2BXN-!ZcMFi$=J=luW3HW$~Kw8>4(i6$Tpy{=+u#tGc}VSO@fx{i=j|DY|@=%IDzS?(xFczjXpu76Nni%bZzf#3=7>$%~v z%M&~_18^l#bMc{+-);eJ=b)L@7Ge!-{~wekf# zIyQ3|5Wg#MsD_aAc6aDnBBfq=$~gjv+`k*L9bNnfMY8{W0GKaO3*T4I1Ipx^`b*OT zP?^3eTi)x~#B0z`T+G9CHWG;wpr7cQ9vPRKJfyX=gA}O(d?!t^ARgd?Tjw9Nz=Wbf zw-RNx_U6{Q*p0#%Cj-xTSMR^CJb7RIC^0uB@b>D>7MR`>zV|=Z5TrEj{ae!6)P%*p zQW54284g*|Y4xlK=n}Y9(s2*m-{WJ=V(9I71I1~T`^@kA@M2~5ln_}OE%-L{hAjhx zc(}HX#r-?2G+JbTbs>3&tLLA@Q$r-Rn6m*4$~z&!t6|ME=mq3 z+^fQj&c<@e9^H+}hGLg~`IT7OK_(RGMyahn6XRNC1JOhbNB?+ckNXJMytB+jA>++F8Hg7@DmrVKS-npp zbbM*~^xQj0FG1jB#Mki$srFO>yaVB$%-ZpZSdP?Pf(@1yDDp3Xg@=GH)y2?W zc-T8`BpPp!q13Y&xOBPHVpMS!7p|C(zA+fmG^7p;*?5S1t>#11UQ+-vRP#J$H=`7t zXxpMWL$jIB_ZOba`FPG<>E#2qW$4W}Aq~c{YKTZNw+w!((C0eb?7w1JWM|i^mKuk= z0*nXQ+gc0thf}G+RH;6FMZzKjqHANj1W;VmsY)xPnz%Ynd3r{5cAPT?F3LEk{vO7> zR1+|5Y-yi}KHoqzySHIkWV4eDyp}9RU5jq}!rcSI8LSp0@5gXC^4Ytr+@IOg6a3ba z(3N+q)^N=8v9)ogBGHf2hkLs&bb^5UqyVk2#FJ+rL%w8#$}zUQeI7yRKE7 z!!{eIBwv++2a!(r(tu7*CK1nlaZumDh}G+6j-Hfd7zX)f-k4@)!EP6k&(qGE===`f zW%(&*!8VyUqs2=x@9f+T2;dxny9RmR<&y+eh|%7jaPU^>F~3{k6i^La?pdRm@&JuW zk8$35{NajdM8u-DhICrQP;z(2G>@C67aVFf+$rhg&yEY`cbI;Bz(?LS{y|Zp1m@UU z7A>!8AaCG@)U|`ed7thoL+PVPmei%4(DbGuNJf6{!u~Tggr*wybM8`ghKtzPXy}zw zB72&$g{xKGiH8n(vWl?2S@|U?U{Mw+ImJ1w-sqoUiCiCPZEi>$ z!+Kopl#>}cj)BX(HJ=GMfajaa6&}ERLuRX&0hP?Lo(+17yCw9ENrBlxG^X$wxS2Q}hL4-_cS%7GMwT7lEi_rcY;O)=y}LsOHfsD(&roN^5AfuHKgE#pyq z^1!j7H#8FZ-j!^3`$|$f_q@2U$?N`GB4}l}M0M8)FqXSzd8}e=sP=wW-^nROq51Nd9wunC%>g{ne0Pk*LEi7hG-Nqk)4ad%gZiNk zGpvFPpt4o#S=K*5E z27S1c+hKaw#*@Q8C~`B5T~m9)^P06WplGwW@BHy{ul^_yYvpuX8aOlns-uK}?QJbh zV37UN;!zRloXd|;Ry2K7`Q=Q$Jv-^Y>O6$&a)rHc1=oFh+<#CY@+jyAO3TeMCZC~G zksPz4APX@~FWvK4cr|d+luB&7g(`dNL446B@Xnr;cR9{2(QU2f?sWbzXD*T*$PU0! z2E=}qJMXOA7}jg51&dlFP_k28J@{C;JE^4FT`1PFl-mdL&s0=#p8%|yVkJ&k8y~1y zmOi+nzbuxPKw59gKKA+LCfo^TktkUNBW84;I#+w2YNBW9UO4{k~`!Y*}2$?n{+^%ChafQlm))OYx683uy|53hNQHj58<5v0ODa zp@xPpGpt0tU}tI;Q*I#Rh`Ww`&~ISP#`#*UwN(UOk0#$YV3X$3`=}-!YLc zc@(Gui9<4=W{bqita_Dv|A_D2>K_zww)|?#Tx6k)h5S}Uy;}J{O6H=o&&WnHJs|jh RXL$c-vHX9#68y9H-vG9J1zi9D literal 0 HcmV?d00001 diff --git a/lab2/images/04.jpg b/lab2/images/04.jpg new file mode 100644 index 0000000000000000000000000000000000000000..344f13cedab65c564e0bc513347b6901c252c4f0 GIT binary patch literal 69814 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 J`;z{i{SVVL+gktt literal 0 HcmV?d00001 diff --git a/lab2/images/05.jpg b/lab2/images/05.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d2c2724bc50c878caecc7f66040dd8f6e3275dc GIT binary patch literal 62013 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�FE}frQk8_w-iG!UTRB95+4seYz@l1 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 HJXO4?-m*kkeM#< zzl>(}K((KDrW~Jj#X>G(a!ni>OIL#*53wXv1mCN`gkQ4{-11Sx2v4WJAs`M8?i=#m z-5No_D89A`qR3EG=&>!aGAg7IZ>#r8f{c&N+jk4f5$wk)PbKxb-QI|pyXmLTXr%ga z5-BLnwmnBXm91NOdmV#O`BB$y$6J3k>z{W0Zrpr*nARwz*xae)OtKHbDmcRIFit1M7J1#r4N^>3f|d_r|f@ z{=g|<^>tKhlqi@E&8Trh_ZxWe!5`9D6*OFIC8p45J4R|n;+41OZcOpslkyHOtzl3r0kZmW4hNuj@!Mw z;g!?Z9i#;uj%K0aD-44^)d@Ncc#>Kz{7+-5|5aA3a-P4Tp|wlzS6-Pcg-T;e54P?zlS$LBvjpVq51_@N1MVT&_ST$Q?+Ox{o~dH3sn zE{D)l)DK&v$O96F{48%xk3iW{MF+K)^4=?njs^>LjvmGZif6;ubooff{DGGgOK5qsrwB*{QccSY>&St zDb8PdaJ$H5F@EuS+P~UdZIcL`%D9`!8I(6$)nc@pvaEQOm+pKFe@5FOi!36Z+ z3CrD$4loBt!AcjHFfe*LJ60Xw!9x@|w+9J10Ld(*s``^!~A zmHR%JFOIv%K-$K?Ou;>D-OCRz{EVe|3}d%4-lh;!Hn~2}aM{%L z*IXJ{B)31ilI3zwg3-76eRFl%{D-}~a_y75@VIC;+k(P1Wj`sl3x;WQF+8Ps2~RIK zupG^qV;Y<2PbpH@;|G<7yN5apt{)7GbUTW4W9gdq1S6~YFJaYe8Y@!!y=u98zH+)J zQ^ooPqeq8me#VC@)9Y~a!oXx`Cub&f`jb&mDXgT_`7=$I*qm@T+r_s>#cYW|l1SAj z)vC|Rp&AncC8at0PN1LO*6(UOr7K8O%e$qz1YWH0X)7}*;-jt`)3x}}{cLj2i|hu9 z#Y+ow-U1z6gpPhO;yXwUkrf%A^2Z^bGrCDHzLay3)Uh|{1LFvIf-d>mq>6Q2(@SPRgLQYpTqr_e_YbFjqzN{W_%jxbkxO1_2~^Y^N%J<-{6ZoZYo=~ z%tml!Par*(0|#iYMlrM`Uu0e6t$y$DSv{41vMA*BYOcP)7iHo#m6nijqkArtVSFQy zrUrE>HAfjA@t&2xH{l#Oa1na!Ecl|QcfpUA`NwOaIniwm_%JLNZMJLtzU`+W(mB_3 zu6t{2?#ZY4);D>^q?9Ep4|3A@9_m*nm&O`J@)lj^&Dk5N0jD-QZ^&P6nQG#LZB8Z5 z4P1e61MO>1-=14p6yGhK4_QsL^{+3wujTtng$@i9ROyX`y2H2(Cb`3n@d-lZ{7oO&omvK`S#yba z)Af`4zK*zzN{MaYslxp&ldi`VfU-We+I*91U&Q~S@)W4G4i7-1L@v<3{LTQ`w4+R_i5 z+4C8f8w|U}ebGA&NZ38_#Il%-3Z`f&MP+9oM=N5LXE!GeXuPb#mV#W#dLDtq1T0u8 z%kf0ClU`fRmqmiWuFU8nF_6s;iud$(Wh7{&&NvOXY^3V~#rv!eS#B%eM}%qm6|Q+t zoFho1r;`H0j=Q^uHRkw=#pcA z_+`FiU4H!x92)3+5y-QF!nVNmRhxkOe_5E!e_K=@HS_+?C;rV<|2HjYzjwEPR*hfb z|2G37;&22xi@*UE%rDXkCO0{c=x~6h)^jm{`lD9wXWB@&S|XWdxiij#jj13xz2jka zrKOXObjNh)7vq5vMe_;E*%Wp3EYQ-|O*M7Z z8V3-lUNoP8EtiF-ITf@BO^WgdYMgJgxVZ$_iv@+*@Pdnq*BKcKkG0$$} z#C)!m%3rt0J49(rbe^l%pvrLDeI4W^KYPoxqU*XgcM)CSMOQ3g;s_6uHX52Mkh;vO zN!{UQuY-b z7S4Zn_-w)3-}%#ge?rc!ruz=$`!vtvTCBg6nRSL9UDwa{H${i!c>|YIA?fOSmz_{v)kgeV@8|{9aTv5z)=ccKa66#56f&JC$7FaR6%{;7hOvF*jP%>at zA}vS~arwkI@`F3J5!}Y+i%h zn2AF+HJt>)3%p0cZ~?TXpDR`}*dXg&z;^`mskZ7hB9405DZ%`yl(AXFxinP4+v#kr zgR?S8VwN(|*yM-#wQ9aO$qp@Izy3y%#RiWjYz3F#lvg&@t-cipRTi=W= zZc%%@D%#2|`(ZBXOzPsCWxLjTYsoy)>;~FzttWT>?%ZSbgemXA_)5zHqMd46^gs_w zStpf=gKtxx*hmWPLzht6GSj92zv(g^x+kw=s=qv%XH5CFXSak@X-c1|Ic?bWlcQAg zWM&$)sGtkyd-HuqpPTUeme#WLbg{qk9_L&4MH+7{tuf~#R`YFsfngui=MQOG!Z+KM z6-0zm6d%wE&@*yM4mIMsfc6qVCCzq;h$>3jd%4o`@-!AaYS zr~@i+?S8*-exQL%zopnuIg6&W3oSPgg-+n`33M)ysY+vDlh4#urciv7#jv`mR(vT{ z0kkT9Y)#%v^l_!0oM31-6w6qnb=A93?VErHyWTOF9LczA4}N(p^IpK~?LG%ksN0(~ zY>DGF-}{M{Ti(vkzF~k_wQqM-qHt@p}Qz!2CH&3JUZ@i`_nHBl*k^L;@^R`nM2-@CAicZe* ze#UPv#+Mq$ZuAfWsxDAyuu}Kp6tO6_E)SxO^_^I}26K0&G1%Y`-COu@B|aBPE%$cH zv)59?CkvbukKyOmq&xrEoo60XZZuM~Ded%T78rFT!&&|VIi(-Qn>-UY;KoLAZHABJ zXZWmoe{io<%_6gyoND{Y8D|uJE76sbXllIh@MJa`a;x~^ns4!aw{R6}8MW(&S^QVQ zdIFO<>!!Prtlq6 zzin#my{fWLU^_W%y0c~B`?c{C0LKm4cs1^rWSwwbDdrKW((X;Yy?uZ3vHS^HpoctE z@LieF)YQncbJ;kmce?&Pu@8{XgIu%rC0`zpYr#_o4sn1tTKi|M(^!HSiLY}3;^s^( zh0BuLht1@y#sxdu+brg_ZLhUbo-&%2C=k8mcDimw5Z{kpY`gF6toiK-B=bDL{NYqq zf`(%RC~!o$=@p}Lirf}M@O5j$o@QUqa73{=v$yebOEt`%!!r$K?>V~R>BUbv12>Fz zx#vm~Xax`_FSqHrRwjVTk6A{EWk!7dZr}5G)ZLqfRYSl2;HQn};=M1Dx|G8hOx6Q{ zfMnhTW9K3TM!94idn-K@4ZrxIYfgp6pCxYhH7j<`>mg?HLX~J0(&MGwST^?Fm`1X) z?E@MzS(ddE!2H)GkDpg^S2L~`pYaA$ja;%hv;I6298pC?AD9lXH7_%q6^wWw);btR*2E7YEl$H;^NdN4> zalL6A6is8eslZhmEn2!XR1Uo#y-l9=JXU?AKQLrYq0BDr#0L~GnGA)^9#l+U?rI;& zzHrA}@j`?irqx2y&fH^c8_Fx|K3HmoxXpDB1y8%g#KEe9kGztTsm+OR9GO@-uHnslep33Lbwk86@0M>$Hn3Vo=P+6fryi z`0BglU@Wi$D9&Ip=Pi7|b1!-(0MdJ|B0rj%+;ZGjd*M9ac3%BcH;G4kBnKSUS~#SA zps|8A-+RPy1MT(sySKK#LiXmEgXeuDD9==^E561q_dB~4DhXO;+px_6HmT0OJB*cH9_~~E!el*tf|;L`pIOpn&VtBZ|1;KJ8MP7 zno3)?qx17LcDzsf$HnN-+%%?jDKY>whrutWEG?pF$BR4|Ug0xC=|^SSyMvyvYD5(W z+@h@gqEHlEu|ig;t*veb&kopAB_Te(FluvYhlO);Ew;NSR*u>aU>9Z`aiB zlh3ujfXET7qF(FK4Dj%V(G(DM!im5X-~LzrmQRJT^UXRzyWGDlVuTUnk<_raY2l-|Q=XoZ>$`b<=Jap z_6luM&qYc*h4|zkmrp^jtR^gCFD1Z3;%FWNT&j)ZlM!NN#I;x%wDIx~sa_KP8C8X+ z&v^8nfcj$QtY@j3U#|^*J=OGH;=a#SAjN4kwbvSXWPS-r9gu69Zk!UPVPg|xY9v8? zzk~3KtLl@Zq))S&Y26NjN{dZCOWT&qJe^P?@AQroVTTruwBZiVLWevUERCkcb?CZ` z#mq!lX^1k4;Ur4d8Zoukl~UKBQeE0ZA1Juj!r>^c+#W&0)?A|=2alt9LT$NkIK1o< z)430nwIt#U%Fds;yk0O8^C; zX8#Rk!548Cjpw{)e#_hoJn8U{^O%tmjJzh}LO18uoeA+|U+vLlzsr)HL8bebc0yLS zRpM=nH#r%b#1PGwB$+f&;_0`B>%Kq_)p8)Ti@D+F_2ep2KfKG{>z8_y7WDOO8h!DG zK2K~%dHd`{t$&+m?S-Y?y7(5$%4*^Aw)k3y z*U1zI(Ktr#)?#?pH!kV|lVV;b3g#D&On1G0ASt%H2{w$(u1|Eez?Rtg%&Hs9ae%jt z3Uy;*g4=${X-DRs*G2kiDWvO(l1V9Nm{(h*vQx{ksKCYZ{HaX#%z+N9SoIz~na!lE z82m$MlK7XrrbLJRqCUZ-o!D$p?%P{hcPAa&Sg`qElwh0o<^tW6Xuz}CfoFFDmNky* zCj_#-&!`iG-M$+Wmp5HMx6q=H4Y7zg>uBkz#K@skDo{>yT?<#Hs~uw=otRWR4qPs~ zb+6CIIn`iXjv=mYmahi}<7sJ%oSkRUh{!cSMEF+H1p$Rg5jyL>L%ZP~QT$p@KEldi zw+*wBUqAYi5lc<%$3f+u3S|`AR4 zsmNmbw>cG*O3=VIbDAchCDw~4M=!^#ruUAHa*ZB4gG8qlD2K;QJmr`)>tX*d@#`RVOQxuBNvb51?9 zerJcq-bjb3$_NWdE>^rrhoq#lhs{+D_}cUV)wT0_;BA5>J;M3Tv!1RIHZ)zAFj7WV z`}m#c$>glc{;ZUhpA;Qf-t2i1BAP?8EW@EYy}-m?ItF%>+oC*Q(kCmt#2#D1Y)l$U zMhbo85Ur@Yl69yFYmIeZ3=brag1ef2z3%v~x_YelDwTr44ikPm^OeK4FBaNi8VP;p zW(Oo?Z4cWrxTafeSaR%Ra9|9y=YsG#fB1;c%NceV=KBeAHflm~!2Al$d%u{Os#&L5 zg>nsK<`(gq`5tmY9&O0Fph&TFf>+QQkFX=>)W=kbhNlbQ4{J`KxteWLsF?%1gzKcW zoiO(VT?U77b>7Q) zHeE$l1z{5P6Lz{yqNH945f@omfk+NZmQh!rLym!=4f!G9%9DWFXHiejZ9ihmEa+!y zou!7|(h%s~@fPXjB2h2;Vo=u6rF8frr?>4Ae|H32bOKoW%3nvUd3-}AWnJYR!v}Kg zfxYY7G@$nP!=7Q*) zE|&agj|eeS3%JwnimoA#fu-ikbKc=elk%sR5NUlm-xu}Tru}%KHfVF@racVw%Hf3~ zV{KFIo@dGJ;8?Sp2JGSA&SF2I3tV;+QAL+ebQ$JmT9LFIKX6L zGcIQmjkCo5lN%c|1VQbqq zQ@1au6jHQchOT`!aYW^RyExy-Bp~Vg7m_Qp(ZkWhFwUfh&Xr-kQa5WvKgyVd7){Fp zbBs1QD8`LDnNsx@?tOzB-V19d>H;%c6N&+r8iORLSyRvw z@LQv+m5IwbUB#s#Vr>@#t+Vgy_Aeub`5^abA+6EVg3dmqZHokG8$nv6??Uo2$Wou< zX0w6&VSPsKV@Aomt6Hp-aRw_-)UO{g9C$F8^_+KJSf1zC`Rk#dS)qdq(NOj3bN%2Q zuA1rmI(@Y%KI3rirK=kL?OI8;M1;*e%VE_e_EF2}{1WX($N6x+7Kh%0dwKXR3~FDDens_zp!fT!70^D^1dWt|q92r6#lOURnL&lmJ&3Z3 z8YlTqVUhC|VSU>)8A|HLr~XFM9#nS+x{gz@^pi(|#3uF*X#qdas`x+6wJVYd@at?L zBaz<;9_Ed3ts><$vqM#rf3W)%8|eJ7B;{I>tBCX6zvAaN?nn2rj(Lcmb1nb$g75R1 zc>d&K1!p^cuK_&HK#MHP*>ZQ{y)lB)Nh$AvM*|3p~ij%DX2*X@qDx}P=P=%{1jiwal^ zjRVhDGsy|Pxi*Oe(T2dB*RE-%18n841eYn_R`Bc;TkSEwyI@0ypys}UDKR&I5gxnr z_Qz>fuTLvP^)GX$%343<&37-EIaOSfb6iGcPS3Si?%sbA`KdW`K0vFpEaohr27>BX z)(lN<()09e;hlU@TN}(^8GEZK=_W5bVF5+8H$#`+bgZl_GC-dXYH5fb-cJ4>rhAnz}(A3OCO<#5O|n1vhti%#xlq^XC5Ltm3cp zivNV`66pV)qmYx?Lt25~Jp&=%4oeXoturSJR48uyysr?DJ!=4-k^70LSwAokf(og0L&=OIdkzJARuG^X02*H%~u56i6s3i-EJ8tWp-ZrXp2Z|Anl z`|v&k0Qi|Wx6sT9kBx%Qja)`|pBrStZEGtG${YMe$o%^d&l7Ij>j=C?eCl?qJCq3I zF+>}802x;+tD>~`Ut!X1qqJ!4R~Mc?BzzcmukCw{0Vbk%cdB8MI`jnFTBcL@G%;@* znzbsOlIoRXu4xT+6ifceA6{D*-F4GX7@7_=DmKemrY2DWpDU`v{pMAlw!8^2lTmke zx_uxJnNXZ?{(6`I!8gsOBc?@ww%C1u`q&*VP$bYKCVF>#L6c6|H|9n}fA-DOhcwKt z=GQ}d9<|e?-c9wx;-hfFEyZ`#yWBwm(|AdGFbN(k2OOz(oby&Ss~>$*aV5M!E4ScA zHE~tk*Gu5F&-0%fTA94H+7}4wAP*<-FB99XKB_eo6yO)Q8B1ZGDHi{d_FCWTtT5w2 zGh-RQ=g`Y4yVMwEwx4KUg?;?1@Voq)1n(PU*`vbn}qKR_0)Z; zndFiDI-=+ZtK17so^Eee?JVP7bgG@aSz(Yo-M616ZqW%E-h%7~2Jn@n!v#{W^_ud8LFNnLWjg*rPCnS6GwU8DzZ@P!y_;;!bcB{|pz~iL}NHs_*v+b3L zt8q-L6txwrl}+kg>!M z|9vSjlr00G^^N6?o!Np->_fX1MePLT8*w3TayeAR=KXI#o=nQ$w4X|dG1jbaX)pje z9hxeG%*F?~g}A%7Ff`J!1f;t<{vu21=Cs~Xo%_htTHuV)J&{Gz-e4oYysLNLlGI(5 zdLcm@oUwE$b*`ad*4@&RDWL_6y&8F_QAWJ(JN>o_pRa8<&D3C;?huiOq(LU|w}pQ2 zy4T{M%J_38<4w>TNy%a`T?_k0CP{pbCTAy~GaCxlQXxC4X}U-)m4# zz?j_|&*N7o^kM6Zm)$>t{MAeBa!gQK;U%__v)yx#f>UL;D#@SODbfPu31Y0FyX872OPm8 zd;0BOW}Fgf0Xr3IBiW|Nnm0b#3;jy{l3rhJ`X~qZ}Ag8 z>DJ~=Inq&c!V!MJgrek0skOQs?|`b;^+p@pdTft4{f1S}h7EtTvp6N+3em1c*vYZA zECcV;h39xj zHYK&i{kh=T)8k}I42ZTYS~MDSW$18f0GcjSMvQi8kD7C&<33ZfJXO7QH8hqtfLxlQ z7i^EsWu%j})-WVtqozUS@p2nzmoA z+U-8`!B0|@QSV!n-%)d!il{qq9+?V6N)(>N*(cbWL!pLz-T#h3|3CRn=l`9jmzAb5 z;nBNeW47;M2ER#8ysIleH(6Y5hq|VSQds`suQB{zail%WdXKoIT;^)G|J{cy*K?BJ z#iJ#+sjt6C?&fFR%u(AQc8|CX{j^x}mb4$=U-SJ&`WEnKSpL^d@-8>lUh6jK=gcFb z0`li38h`GDCqleVZ3pN74Eldg@4pJ9Xx<-P9`uuS0uicU`Mj@luRuC@BR2@rN5vnl zj5XQ!dHauqxgGANciVqUSMBB-ueNH33jWBCc3( zD1S|b5e_2k%~OAi#D+>v&~tv?vFgB%?}v^aL}Ua^tHVim)t3yo-^nqN5|nO0T-qOBKEtJbGF7(cX0=G=|L8t7fohnX3q) zZ8S@hPubmr+S_>v*8SwQm%n@+fjP!Ys|9y!%S+3)tbER2wFAF@(pT(pTY%#g_2gC_vlN0B1L_DXz&eP=^c3j>)qWNrag&Rml>0+!{_e?Jzo-J~etr;FXA>mZ2 z7~F~HVy~iAdRQRzH~F^q6t?^3v^}g$f7wPP@oF4wbq&z!Djj$d%)=H$5n$y94lz-t zaxr?9{+hYy_8RQt`1}we!a7eodG+El@u@&|QalOL1pIQTcD^+Kc&kM}a&h+Cx9A6) zfo9dT?1MYP<1jbB9Ai`YVkrXeh(0HbYe5 z7s>XBMU2}PjJmV!xm#Gdf4nbH#74zo_MkHQr*z6DT0cJrt0SLkHD9NBb6&O z9`KQ*_VhRsJ6=q@nE$A@#)x#ZZ_XQ z#;sGQow>?hzeA-TD1qu8HRv?`!(xVuwkqcKh*_D`(lfW&!jWUaDG_TW)VA(zsp5SH zL=A@g>dNvAUF3%9JAx&5vZ=Fwav9PwZzB#RhqoB@iol_#afr1=!TAd`&? z@;}!8^3(!1L@&>rQpGx;dt_d6cLG8Cg{kZ#1d6O;Vfx+!@LN~_qUoqj5&;O#JBRV^ zZ13BTf1lenYF%-xX1C20t4`~=CPR(QdkB*Wy*zj+>S3Ny4;T{r3z_is@v9}k9 zYU|@mZ^X!?d5=VYlt3i>sSyC2quL){JrWa+9%s}I4mU}U3Jo|hwHY#9BC4Ojdkw_Z zKrZg8wwWtrI~@km1xgcEL8Ps7?#y&+er3(Gj_c%}HuCQ7k>SUQmef7tFH)-v8t1=I zkYd`r29!MD?1`sgC(HE=hw9C|5X{<7Y7qft&)H zhUlUdJr#5Dy1Bc$aRw3&imBcfoaL=NZylU}JlRvfkubOr)y^zQoZm=L!6JmfQ>rj| z+h!A9<#4?7l09|};!2TSPM{mwB&Di_Y=zFK%rVYh2;^JFyL)2374>@mMC4cpBd|$x z!JH`Ttml`vY(dsF6QcW4rgf^puZHygZJ`?PF;2_Odz#Gbk_z*GI?sIn6cN>Wo|QGQ zvt@mitqm`4CA+^NVD zr`I68x!Wj1P;2V=GV)8gjzuOiA0s0B9wx^`?)uOv7O}opSPyD+Q#p>!^(TY7o>unU zeK1kssf9JYWKRH@7*{T572{lg zTRCts_y)oYlekx@%Tmq+03{|~4IZns2J*c#HseHpy;7TcYWs?j$~u6}4-sTtQ$-N1 z)uqjWQ2vt;e`1(RrJk+x9+56SV_#pb#zXGv%~)b?W6(|KyELN#It2g?yvX_U`^iUE zPbP>8;)w-#WR6rnHJTZW`?>$azJ100BAbVQ&}QUGsp`N|61BLeF~M3J+wIx~!uzPn zn8DVw$cT#hOrWy-A%lH9&x?O;VN*0ol1I)j@Q4C)-MrZ7Y`S_c_xtDnpE!u2ajhgj zHX+{2|5nR=SP1w_ui0No=t!zy1%JBX#XAgIFJER%d3y;Kztqd|=txr5vBo=MXi97l$x_K@6{0*dV%_t6>W!Cv4=@F7V!fh9@5{-c zyAvAmN_JVa#p{RIr+?n+UeFo9$b3%dM0@P)fcvhSN1yOV`M?PmiKM|5^&;=1Jc#-p zt>`g2zO`NVOQAV>>&K*9!k^q2$MR^GmFf?9l3VVGx%P*5z}^DeSk~G8o#f=XljO;z zyUOQ#$X;zeD-Irq`A0NU&~HD&pX!l}H4 z>$$IfNKJ4#a`NR`La=tS;^;*4!SUGsn4ch3SmD*1cW=t`q0Pr|k$KsyUYzQE3BKyb zi3_zaWIW{Kh#oz^>f7&o9>@E$u6y19J|kg5_zBGLitk&ok5cqeK#wU5H94}C8S03% zET3x|uz0h0w^W{-Oq7f#|EzX`Z16{TltrU@562D{M@CzzT5z66*dtwxv#&VKZNOox zM8-j7css4lBex!xTNaM8OXYw~eWjmc@$>ghuUhC+6v}>jcCRtpf@dPJc`95@OJ{VPsbHddmIavf z@F5gYn+L=@HW(hn>6I%TZr!SobDIh5y35~mNF>gyoF*%WY1PMrmIg1wefiPC-H3-t zhu$N{u+tzBS&|C(yNtOsQ^&ey2ujUevaMhr+)FpN$Ets3xgw0%Abul<*{*^8TvTni zH3l1Gd$DN&Sfv%z^mG@x{8Gz5$?4kSQ{5EuX3^YT)BJsxQalAFmFh9^ah~c*z`g0k za}f^Nkvk8_sUW1seN_VHw65~37c6fzta=6)?X~D{*YvXEA;FqB-*PW6@#l%elI$VMq@4ON zEgx2g+E$+g3KIU_T*(8~u3`{S=v?eX;x;<@Qdfy_XlmOu<11Ob52$Is(w)-;c~&oc z*#3@kmXXIp;aUn7@kr*Ze0=iY0DSe$$nKHNpV*qSqkDOCTgc=Pg0c+BfoXMm`y-GS zHBwj~ll>EYCB6Om{FFN3Z|5yxc>M_0;(F$rAsR?#Oxr-A2t;c=i`v*zyn=(NK2mp* zt6lklXm5u+*4CH1J~B1#Mc(~#N;JQz;z;#f&4mbqdk>sWT8 z!Apyys_zccy?ORq+|Lvy_~-Vso^iNgc_>Y5)Uj4_T&f+l**S( z0z3rpZ&4o=H}oouDP9l{;q9=)my8BsormdBLvAa~O0%^`GCjHo{K?N!WW0K9ZNQx; z>N)LO?!;F;?H<4StC}2a9svv991=Vf8et=w-&F+ zskQX!E}YpJ zbQhW<-VnCfuWVhVw<*ZIyo;O|WY6&QHqZ`f0Jv{tIotkX`pOz z5~_H#wXIL&IYkJgoeepR3KS}9F6uj6aZx$TC%#r@9QeXeW6q#yr!3{rTgvX^EJZ@i zYhiy=q1=-i5}FJ)G`3B3yzYyVO;x5A&&G!r*>DP@xW5=se}1Bx{>pH5?eR&+=Eq9S z_;~HSwTpn98P6N&1f1U6I4t}!vV>pqh3)u=r4OIKQE6xWheQ!n>2g7?;T1Vy!k+n) zC|}D`&rN{fUH+gxuFSLA=T%)GlKZN+k>{nzQ}Rt>sofP3ihVup zJ>u8j_=TIVRvv zhRjanAsVOoA9v2)*a$fWNRPOoYUoIBJJqZsKviTewF~RAH#|3mQeSKuxjP+6RcKs| z06E18x`Kbl5Y)3+pI3S?A?74&F7WLGPzsGbgfYP#Tj&*ermXx%ESe&;jqUC1+e-2F z0u%+0UNewao>_HwXS||T%pv4BVrs@0r(|Cw@!S4}_)CQrP+cfSc-{8}s9AmrhV;pH z@c?_TGoaF51<$m%9qY_AbhpuP=}16+-fkyG`q9Y<+^H)r!!?fMYZb`ttG`YSll9dp zvMEG}Aw4-D*O!>uHn&dYjdDax^Rupnhgn!|K3pWWcc=-LUK=m?hTIhyqtm07-k`9T zVoQn~x5|RdH)DxBt$TI7SCg?*ljh^rI97hWR+vQra2M4Pf`xu;1hW z^>*t{ul0~sR8$W#%_XR|2iZa$a(G@ZBMC3cYzKD7WTOnZBJ6iScfH9@KWz*$E|X%+ zGwhEn;&MK8>7!Q;JCh6N&-l_;9I6jVWaQft|Hvco0)vec^?Z9NxJaG~O4R+LUc9p$ z2(2yV69r=!HBDdnlti`D&Sv&4WU8Dk2VI8;=OKGg@OnF)Lqs=DNGU^CJEtlYJUFCamjL{-$QNjjKPI=q@Y7(GAN` zV`d$)9=fy`O0;(4;thxKBqkYwfSpyJ!_C=r|gw2=4fW$tf6-wptw6h0qh{heYpzSoX zrBKm>ay7cxKs?{yZ`9d9A(Z9L(Wzk@t!UGv$B#J-uCk$br85WCk`tj-;eCUESD&f| zvgwa+eJ)kqOlWX-N^L0dY*Cy!@x>`paVlU8nNM)Zx2?akKUt)yivheRXleOc+^5JG ztBSphj&!=@h;7?2V)cH;;h5-JE_5Lt%`&B5@cD4;_j4hG%)@T)tBT@g*Ji3b?838@ z&FZ9a+3!Wp^qoS(yAnZSmz5SXzWTZQ?c4!4G66-3mgdk~z7dSW$WUcr&NUpi6pfh? zdC}z2W$uPdmebhCCB-xA;3IzG)75D6qCW5I-$)CoU(yD-$e)nurmAB|pcgx;86aIy zx|0`4X$67(;up^VW!L_W~82vS56opdQ%MdCCq5SB$=#%tn022PCXTLEq1`=e=>d zBV)1kK}3*HF1%qUkZa|xG+nRr50g?kY#-Nb%noW_*0$5_bw6sAImg;?JMeJ@S1<=% zAN^a(Ad!l$sI1{8ajtHI)h0{+`LsM!TcWR)mZ{@&TQTgcLX5~LQJH>htCjr9jxc=_ zvNJ9?qlm&JUmGVw68~%-=l8Ik2Heo)rC4=>N@jZgSj&w_!xJ2s++okDCy>*%$$E{b zu0+w2F@KEp180{Qhz?h3oqLmN7J0WB?UQL=s(UnQ;)gPUIxo-DAvXr$nO?mEQet;G zp4C7yV;`tV5wsF#ZFRML$^;1M9zK{>wt9p2>AW!dXY^EjdG--S#4$S@SU8jq6SzCL zo)&)DCrAwWD8)*ptwv*ZPjZZxo{lfVg<8Xx93dIuPd9nO#lbuyrhY@Tu`#|Qyn(7n z3f{&n_gck05|R|;L*Dh~3g%zKlfD3w@n4tV3(Wh7%BK)^EYiG3&m8FVXPl$cw`&zI zGk!C)!~S(aX{0tD1(OS1K{C-BwbERo1{#3=6S#O3`o$%?_PSxd!8^i0ysk4bQZ*;T z!{YtP8-=BaJ;%%Y!y_E1_TdHw7U%r*+Qj!2({faHCB--*X^btWkY{14p@Cvril^gM zm2%aFy!y)ETWKo{7A!Ng#eS_;PoD-%gxD4OtjTxOOu@vSRtNNciOhVQ} zG@n!6Mdu-pd1qWro;3ve)2ijHEbh-Y$A-tt-m45R_K75#Y1dSjCP_+=TyHWzslGzc z*cLrveK~+mUXEo3J^2H9hXAKRD$($*XBH|e=-D4aNr#>U3OxuyM;8$dw8oB#HM9IG zCiaR)WYc40I_F8RySksUOWdq7r*1P$*0GpenX0y6Ss*p*h9u?;A)(!VY+^vp%>Ojs z=AwT3&==c7DbuxI0lZ#}E(&Mx0z1~RlrQe!ksT#q4;QHFgo!r}b%H5fOs89A{Weyp zu&jm+Z}~(;WoI2Y@mUb`oZ)0PHl9jVWUHmG!r3^lP6tAhHtv|_`P!U8FOCYEg6?0< zo+N(zc-Z>B|2cBi3BXrlU3-Qv3r(>Ays~oLWM%28?I2-wUWzAu)wOQ|&Od0D`@|f~ zz6;@N%C9-S&arpCzKN@rXG@aMr%C4)GpC{0vT)IMReK3kwuUO39+i=^?IIreQGifO zHlX`CWxIL62oHc>;-PsGjVlLE7=Pb$ZI9p-*m!#Ax6Y-hg|gi{hZA&~@K$x&)s&cY z^cfWe`%{4Y`5={iX&Ac10$=DSb_Qr){1DJSLW>93kZw1g7&}z)!W`GfvkKc=%w5X5 zD$@qbf1PYLfow)q>N?zi6)0nFQNb^aiBkNEy3P>oH5SRZKYx0rDK}ar=|6Sq^Za15 zEoa{O>~=RcEB+ah7k@_}T*X6ti|CPhb~kAV-V(l96&J-36}<%5-mEI-%X)s8dN;ag z6nK0j9J^_;gY^zl;ww4rnt?qcpQL<5b*K>pSo3bUJC}7V$~DI~%;)7w z9nJS$AM9Xy4))^u#XknnshzzNr{ltGukrCi&HGR;0}6PLc$J!Gx9spPmpCTKRDI5e zykig+I1B@HH8fkcZC``I0K( ztxRJ7Qd$2k4}I_wD{mP}LTn2R;Ft}|Qb>TB#6kD?Tf_x>1wS6($(AX;!l5A@;#N}R zKp)TBc9!ZSn831yc&i1q651Epu2Mwos?IvJFGG@(Fi+)XF!>zDI>==LN^fHwq1iaN z^R)|?lm?I8W|c0M?Dgk76x(t2X9LpOXR-aYoN$Z{6$y|{g|B4kQrnDO9lqW$_~S=x zT*BMt>xwmLQ!Jn)J{YdySgfCgX$Z{T5}cX(Kk& z7`tV-;1^bG=k=aTCt8P8FX!RH6q*OAh~_=a0o)M}5Q8#Hrx=lm_jRQ=M9dz#bb<>r z)rr)uNl>2VcX=p7La%iC_z~+|K7RZK53j7oJ7Ih16 z5{!k|SDj0ml1C=a9MDz&ax~)qAvY#$(-yA3xGKHj&p=zDRyzd~-Xj$D{;;1#(Vz62 zx z7w6XFk<*nkE)bw>@fyCoRS^61;G$B#Ot-RtUqI!iQ5mMziXhhJz4@~S`FBfX|BL{f zLu3AqUVGhD5~#Au^Kwuv(dktX_gz&O2W+P);uRywi1Q|^ym{F_LnPGyh~HFbMi_m4 z)NIQ~crhT_K%ka~)EB#m;tQM!aX}t=<^K_O0e#DjQ3zo1LDX@%=Ma$3{}U($VU?x$ z`lx=L@c6F|C*ZGeQmW@~q(%N=4S5DpwAw5eAKkP1oCP*h492` z45Q=VV`7jjp@xzu>9DFHf`+V3^LOXxFE!gg|3>t#x$(D5KGh94$FKWSNw8xLey@We zdHccu5q=hb*p--`Jo**?Wy3NN{{oMKWsFF+CNNVEG6O!r$LBOrF>h$T`h6*#Rew_B zz|1TxJUIU1YG^A}i3MR-!;+0M>pDTHP$szuFnuq8a3cLBVV!lYezg69J96d~sQp51)@$4m$nTtURCM^K-Pt1A zPwQI_og@_w5lE<>Co!8s61QRre}<%5gmptAV{{5bRG6-&3@gQFoZ`cGxq(cr9Zj>x z`OPg&Ne3HRO+_h@qFI`1x?odW&*qC>GWSSJ7s}R;K%yfMN&^)iwWxw3(1D~P# z(pv~VUlal6tK-WjWq(b)DJ35)bW-@0hwRLL#2A@aPIi2t|Td}8qS?7 z2Ypp~enUYeN8*e&-E_y&7SV@F zh;)R;tjBe~?M_MFs*Ok7l1bmS*8;wA%Tzl69Z;l7>aPXg+j){m=)SAiS~FwkSV4Dw zR2d|O)rhsE+JEaEBjZ-8?Scq@Qu;WAxLzJ&YQa8`cr}+h!&fXZ<0G(1;WNh@!jRrT zfusi2|Mhwx%H#0Z^JZ84l~?X1HfWELz@PM*bm+R|g#DOrVG-d5Zm16--$)XOZX$hJ zIbHl5F;D)TLwW2)9}7`pZ9;7GB^slD z(s2H={DYyIG(WB)y>Gv1qp>Cg+)+?>$}}F-fMu#t0!2BxuV;(?sX)bO%Xl56EjiH1j*+s2i_=I(9DML=s1X3Jx-9oy36G#jy1+~Ns&z0Es% zaqY6B5WMOAy^Fz8VUWL-`D|C^)E``YxabjjMdB+PaNXrpV`%xKS$|QQXah31!XiZn z^_|637eHpp1ukv4eGL6&@lwF@OfoIuRV4;g=%} zM{e8^i_aU1QrMs|C@knN*k3<$%4AA{tgnUoG6M7VQ;o0d!23=hkJQ4d!Y*%JRx&lF z=eZeFLUYndR!F?Oeh=ATMd1Bg_RJTjH$iT(*o9!IA(rqFXL;%kNSW*VIpbVwriQ?D zj<0hyDstE0aFjeOE7vour`bJvV*<jT-d)`|7`~!?_J-Wmp zQcFhLES>~^$BTg70BN&_-Ym;o`p7eZ)>Y>DqS@5}wHXu3H}i{@%~D8~Zwiw5p&s_j zP9e+v)n#hE9*YhIU?U?SnYqoKkDZ>~DzwmMPMvjiTe}8dkm|`+{hUUfoF&SCi|YT+ z%8vdUx1;z6j-3J9Kd|r!kVP(YL-zl(j~}bQldsbFyLlcd{;z;NN}=L9a=eoX`1F_? zD!QaBs3TGV2JzM02JL)ami`s_c<}?H<_4MysxLq?4rqebt`FzYBh1z^VCbyEYKlb&Vp&>(JL`#rWq(1 zp?e^I8e(wyW9>K*Q8-?kxO=R>VEc{KHu)Q=p@Zx2ZY{omlmR(C;Zv-ElDP0d2lb|j z>K#?pl&|E}2Z)%mLqi0)O1*At+vCZ?=F>*W6LUv6dTw)L#~FgOM5f+yas{e;RWvtF zD1YBX$ds~y;YZ&s()Nv&Wwwr$K1Q)V`ior$FAHr=(#NK28I@DVa1J=Hm!JO-?JUu> zo}B?dIbBNV%2v`ip8cNVrpFPPUS(>bK@72--mZz0k zI2KgdkI?L;L9$42vU3Xk_oXkNG&-!7qu!SZytwTM{lUq8NsZ%))+3U?Na0;rP|QC) z3<2Qhzh?-v$hYf|c#eWejFM37ueIPi9j&!iho2g5$yl_LHl?H|!xsO*A`3`f?Or2AIhN9v2EeMwWF)H0`?W(Zj z%bX|AL{I8Xuj8-A%TFG;YopyL#mej+0{eC0C>B9NG}i7ux&b{I)XH;LD(3eFE64U6 zu1jehoTLJ;E!3sZE)K|wJ>~n+x;%#bu#+wQd@zV2KzXdJU1-RZ#5wd|z0bz6h}yXl zlxF!cA}Ixi`#68)|44lf--$I3qzt`bCnq`R_0f%wnoW&0SL_EQwnj(GQ8GW`i8J@A z?+JVzV%(b7(qo69uBud(=x!L_w+EaY-FL9u*9C0Km4TR}_{l%y>6Nbsbly!XDo%l{a#gn#A#LtMnXdm3Jg*o8h}| zSJ1>Op-HiJd-rVkHdFt7De-Y;t=zX;h~VyyP`A+<#HFz5v@HY@0!@>ew!8W{qk7?3 zVcupr`ynmk*W_*Bt1@`rjr}?Dg-50EdcE4H)0EeQ+I+L9B!zN854}Fnz&`Qbuog`t z+VaBvtBh}<8aub9)GgXmh~U>2!^zLyF(SJ$Y)ZBo9RhtbQFS7YXf`**BR(!v(V)_T zM&q&K)rX{MIz8kjc<%`Q8`D)+=52>uNbdyJf~SY}jDHWAVUJ%?kEi`c*3k>{^s;9O z%xsBQ{3$t}&GyuaEz<=xHM5t8MT#aEbH416UrCjCGQ%K;ad9dLF%iyIV>e6wS)%#= z`8j*@n0dMgdR;RfVU!aef4(NGL(?2Q&0RK_m5K-#E^w@D>7nS|#jMdlYtivg4vlQ9C^Wo(wQ#v+MPIPd>Pa=IhzpB8<*S!(Ty@nJ+PgZQdL3`)SF|@RQGlOS_=~H&YVj2(mcU3+CL5t)9YL zKfj_B?7Pt0>-c%&?H5z^zQ$`x#3mUro(d%%)plqY0m80mOoqlRYI)36;wIXHW z7Jye-@;if=u+>j3(tcb4f$g*t0U3ljjC%@YVsv2|sAw6y^AYa>f?2vs{8#lN(aTKlhn&f ztV4kxa5hM!jfh)U%<-79{_&ee$JOZ3x9i805=Iy^r&*WnE5}}W7^Yo=V%UO8K;tkF7y^=?Dz)ZY$cw#C+|MH3YM6I zir7XuCaa09&0Bmv04RiZsg)`>Rmi%rc;C#s-kX~fl{p-76rOLe(z%e^Gw+~#I;M&H zpOvP1M3Ji)OE0*NkVkXgej6$r+2unYijFF0SS`Kx~L#e3JGh(+_bBWYEbP+>O zcXe_8Ct<{Mh$IVILcE8Aumgzy#l?QZo>EjolT!bNs9}DRpngYt|zh&KR4KMEj3jAjkNPVM^oU|-{6s6%(397O*_jh2VYA; zYZU+oGj5QPTwlhX9eY|kUJ!X=Y)rDVoieD8iXmeBSUzIREZ!>>(<=>Mz?oR=iS(wB z(D+AhU5@Q;V`D{j_kC+}%8{p*!GJ`%A5d{lv^;#>o z@X_=S-^kZj#jgUU`p3Mho$^hAU z-$)Io{3TfsKH&#)c3T1q524aj`7dTO0$(mwc(oE4FU;eAy^9L|>JE>WZ*oRkd+KMS zUD%L(RqU*M4v!v_?722@K#)#A8Z-LQ(aY{hcgD*FgvK}P!y}=VIx>?L3Su#rt>*c_ z&7M2dwjzW#jCDmJvtoHe4Z9jS%P=wH50`Wz2M-r@1K5SS@`{1a-qDrz+;hPnCiJBW zouRs;mP%h;KIcVl@D^t!W zCDic;lK9P0vPpIz8X5Lw`-$RO0qkz*EQbS?l)Ny2@_N?u$_S>8#@1#K7O_gbpvMhf z;mvdG$+!yn6Oyml37^t4pV{e;X&8G0MTjFA!jwBcKEKuY z<`5#Ma3CbE)whZn*>lGkQl?+q&z2>C(YWnq?&$S$rG zc4EAn9Q1N^@f#q+GLRzWjtzWtxYUqDojal5W&BkDzAtNgSl=pL)fBFB!M=y|O2rjM zF^#$&Wn{aYEJvXKu?@nEQE)U(7 zB$;9%khCJ>HNQ|cA5q~k@N9DO>o;`Yk(A7IH4QVl)8mo0B9L}$Dt2XI=3bPm8BQBQ z5<|J8lWFxacgw~ynB(;N?_^UDNyXe7`kWK}c)0jHLS%EcRs89bU5zT9N7L`e1WnF(tWh-X*vc60(7*o*&VTS3&K`7MGxMW=o9D5!Dbit;qHOI}dT z%e-vJ(s=J z(b#0&7a=_pzp|cXs4IkNR<{Z?f5Tnr`50FgJ^6z>X%QJaL|Kr&L1{yA@x`(5AI{<# z)KKI4oYO^n{bOrW+!3t4qv%*Fqof}q1QDL;=(F?fd;^n-#5M{E*}U)GyB ztet9X!EuG zoqa0EhBd48_Ii9Yl9Qex?+fa5d2g{93(7=sMVbuEWdjFq|8#Z1K!!5^{Py*ThsU$V zln1&CO2I6Pr*k8c!4j`#?SEOvs)W!YT-j$P+CEE4KCKw+QE15uI*)3Y4-{HtE0~s` zfJGYAw6-+N3>)f=U4xn`ib7ZHw{}@}bdAGeULaASdVxLtN!1(+VDW1jp&&I}VA7c_ z?i)rN!oHFXIn8pHK<#5)wmzhfu7G@?DfWyBOc}ez67+mF84&;C#Gz7il|NXn#d%tR zo|=0zqj%jU9l+&>XfZLJv6#zko=HWU))Z&~xS{ep6Fz_X`Cx!#QEmGT!q8yZQ@+as zA;VkK(ssIby^@*p!O0CoyfnY&olP^5=(sNSOWY~uDxjAd=h;r$x{$AAVT-MId-}|~ zT5ZaUkA`dt zeWM6O?)L_;u6Kkqw6mXu{6-4fZVFRws@9Kc2Mr{8_V{M=mMrQaH@<8r=_Ekzoidh7 zK3y`JFY+wUtI7!Jtdp}>n$y)+3dJDhL1x|dCgKB{Vu>l1m&ZDqeGP5`y>aB+sOJ(% z=L7AC91A}ZPHeuRXd~Q9HpRA>gVV3)A=oljH6I=oI>Rs&-r5gnbv@4%Oo0>EbtIipyHn=6MAdE zfJ?%)VEIi+YW34MtW6J@Ph+4pU+y!iG)MLuqL1ZHLuBJ+ehw#grsTd?x=e&y&QKea z#N1}liLiFM8Ogf#nIb-U<#?gv8lLx}f#y1*G1u?q2w$x4)+v_x3Y=kw6)mr<=nZTe zRC5gXxnRaGC6j9K+4gOs(O`9X(FF=eu zddLzu6L=x(7Y=D!xk-9>?Pq-t89Fi=o2yM~NHA21W7j0vUYZysi4nhp_cuFIS@5TJ zjPRoVtzu+_67q;EemK{&A6(JVbDOCWaMMhEeY554sv&>41ivtmIs|t|{Nl?%?Jax5$GlO;G>@{UUH4k8a(;HOY;2W(V5NA;IzF2 zx>vi;&t_V~l=H->)3+}`KyYI0yn#{_B3}XY))@$LbtDAdX&bosH5+1p`7IK`%h~es z2<>1gtx)m-sT-e6i7D5;&C`tWl%lRefs!Kd=#TF6R-e&n7RlDQpID_L_2MNDsv40- zt5=(ja+OP01Wrn-f-y0OyMA=L))@P~_vH{)6tzvfkVT{GGO?Y_3-g45Hy|{t%61SxW`pfCpmtaURIzK3qzNtsvWNniYrkxw{I%(O*`U0 zA`c0pPz#!X&}RurWZDbAftsru)L>6EOcqYFdL(d7KedaU#fG=?iAk02-P8-r<0^WI z;qIs(jJ%F2<9iQ1B{e$gv_zwyZlykz?DS_jvktuy&A>MFLoq#NTy0^B1^EaGH2ffn zUPQC9T2v}SX7%N!IW__$0A7;W-}<}nz+)Qk$G8rCs2MV)s9IgeT-!NejvHJkz;^1{ zx;DvQ0^ScrOP32`uVrG1+B@sd#L|!>e&db#)q|a# z{u4Ef^8r!ikJGm~$>h1u%PAY5vLF=rR*6p^6QHJ{2CHE?hMxY8xIlc9)kXbfBjuOx zYIr!;BmkBqB=-xub&{YFmIBJPeP>AT%w8qz=h^&iV%WRSyh~Z8w(ZTF1)Q-=)y-}T z)0$3pH{C?%_+~Aw^$CUNsM2?UdEzYeM^cru4G7dPD@di)V^MIiV4hmD1$gA3nspug znkHf7C;B;~KDO}>_jLd(w2~O8yH-tAz?X?S$vI6;fKcN>NGd)W_dLX?tu-kZd|*OF z$mZTQMv;YuJFv=$E}hx?hiDa@$a=K=MjCSPT_aEvi=?ob9^AF;eqmO3Y(N`4CZq^iiJT^&V*UHCbj1Sa3CLM&10`(g0zu zCca6xB%Jl-?l>(>*o=O)+rMbPQwP-K0T-!q{& ztBk4h!>hefs8is~o5D;GIQ054fqGtq*{B|jH`k0^>u~HoS6bXPmsCRKB8$+|4{0X> zHuuM_gXzv+5{LV0l(z7X#*6T`MOoyi7epMpN z;7h7G!}RbFEUm2Vvxg*&F;MEm9Ss%ZW2{)nlGjtjC z-8?VRg)xU{%~a({z!T45pZjqF{h5GbyoJjTSsGO_2(-EKGUYX5AiR&*@N)rsicC>; zh}xmNS?T7HDnC7W5hHBR6HK^wr<;%~b9xUr10P3%+zqu z%%A|zE{Sx28&on>0{@H&41F*#=G41v14*~FCM!2IS)Oy+kN4YTr1?*eG!folCXBcW zjuZ)AE$h_0a@BHxgEguq3m7xTm7f_HO_Gghji(FlP1Tb`nw2GE~Tg>Y9Mc+-FB~ui1if{R2m9zFp z{5fvtI@bs_y5_MrpRJGHeF>PWWE&4=s-4#3?D@&Q0f@Kkmv}7JxAD$`Q_#A=(iNeZ z3UGFP;8KSH+)ARE7ALf7XE}a7&Nwm@6zD2qoM5pr{mR~}bHGr;*^|V9bC(<2XdE=h z5&P>CRIm%_`l+K6y4vZJ zdPO(B#}8`E5>m9?>z6Bm1L8iARG)Mb@?E{qDQQP{TCQC=1Gc-X@yM z`-P_#??36!_q^f`Z9dSUkSSq`7j9--@aOY=HC1bh26Gu$r2=G2V zA}FBLdws!DjxE{|Qejt<;J97i7J0h%FQ*XcAAiWnk_2>fL07CCUmg%AV-y{> zv&6m&QmY9yWtT!r@gRt0l{}w{3_-HMzmN5pJ zOdi8k!1_QUJoX#N3`Z*-Um4Ynd5HSxo2Ln%CX4{hW2Z%(?Nn#XeU#3u$^9mHr{w~b zA1pz|tTESy)nybeM)K(ccZN$CO-H$Ig9Kpv)r0XfKryA`D z`?0a*%?CC$ZxHSCa|fnmoLYU}T-_(+%2fRJwte8M7acJhhWfp92sQw47V|e!Guw#h zqa@VC0S))eRY&m#h{!AjvNlro=068-B^ye1X!r?tPB2fmNQPxH&G3D915@i?pUut_b?yPgHoSfHd^6ZUHu@1Pk{hrmTK=W`GH=bZ;QI_3Q&`gb%a^lrBt zxnD*FzDB_}Oh+Cc`21cC-$sJ@)YbW+X}CV8VE83SkzF4X$sujyXvxxRjsW>kq`nRs zONQ`-$)s;O!}OM_tFKdjkN^R<(q3Pkr}A6S29hifdkUA-PEZ3^X(0UfD_S};c9-^s z8W#?(90tQ)Prq(HdF!bYp;Wqz+w=7N=a|!U^NKaah^_?qMd<9Q)23g4U(ict&rUO!TOjN=-wPjDnu$ z9L2J~Z{jA4+#uOEeBDURL^y+y`yiNPEt zbqt$JVo#jbmK}OU%`Wb?{~S@+nvta9=xPo5m^^IAOO#MmCFH_=4g@AM?z! zjR1KW&e$5UY0z?dbrEjoc5iOh7kk%q(Tuj|^hMz6s+8ljvhVtwu<3NX#_~x$W23!@ z3&E=w%eOjPNG<~f&i|vm_l#;XdenTeBGLql-jyo7cL>s? z6RDviMSAZf^w2SoPy>W`^FL?SIsf@EbMA+kGk5MfAF`6Hyo>kE&LY|S+0XNPQnUyg z0O)ANN!(K#V2SmdcotbQqp!dC*t`aJBfL?cXgl<2xLZWLT*DZq)no!+h9Vwu8ms2m zD}{wRm;dIob!e+p?yZHeC)Vd&F&bn~Dli{-Y^)Lmb$3r-&GAy-%R#dAl5WTkVs6}| zNX?u31B0falrF>M_Br&$#cW@h%V2Iw|XCrrNkQK+HkcjmF=<=!gY=e~w`u1$ze z!;IHH%{=`lXqB5|@!O9JGH+|n-m6hGs(|oDhtSp@(2uIhSaXxU~r5@si)}>Cr9$^uiienHRma z_wK4OoHT2ljJkoq`QI_E-B-%lUe%yIP<3#;E%cr7n1kR`0mB#88#WZ_5|wp}#a5)|6Yv+hz5$FhW1MxrVSC^qxh+TC z*^o7-hOj>nD*tZfl)mZm6Df$jYn3&Go{wX1itlx^Vdk0Ite?&J*av@Q>^Df2L9Ozau6TDDfuXf031ZPvT}YFDWiR&ZKemCWv=O5>!dfuuoz0k=7?QLCl(|z6!SBF z>wTQ?U{`@1{9VTyC)_!C#`JnyFG2^PWGaYsx@CR%%09s}p=bN@E6IyrbC$Dh&M?DT zf%zn{&lf$ZxKy2%fOd7eq?c3D{ue^-HjVJeiGA?PWIcL^`Yewadb6C7Nt>UUclk_$ ze`k-L5ZRM?CHGTV^@8Q_JAFvLtqJ#5b}Pa}2hj>szJ%|dAM<2Jv2RsIh~4@{ho<5R zp`9ho=Ot%Rq2*5wz2ec)`qzN7sXZ9T78F)zv}*n!YS5itHq*X_xQ~2*M%vY`O>c=hdf2@nJ3HzkO(!a&gkfX@~o`8{Ag{Mk|Z8%xTy#xW4QatEr*#t&d)*?>OT6`rpw5> zm47U9bjM#c`CZ3!VIyj+;_j7WNeM_MjC#z2XOPbGbGN1HAfJDS9anFbM|zL>H~QGH z)UiYa6B}QV_+6s+OGe&JzXt>h+U7F~KkDuFk!G~921+Lg`CJ?4(0 z#hui~>B;VA1=EBie!XxT=qKdLaWk3he*RIehR$fvsQP>n+hC6RtzMAJrIafe+2-c4 zVJfaMO+7ZPHhDVDZoGHS2FT9|;m9tsVXS*ZI09E*A=qRvdya_}kEwhguQw^u+#pf4 zQEzJccF@@(K8%B-fb*$;QO4!nc~&K_DyA{{ipj7lmIx>C)tRi=D&nw}IbWct#Vk(L zZx~o>Zp*oh-v|6G%yJ|bQ0@|P?674ZNk>S7H?014FkkP>+lU4Tz=6&2y}tHYri&e4 z28JJ_JRd1DfY;(R?P3Zi?gVSz8j~zOvfF<0v%WNl)OK~N46l4(!pi))(S4~Kxoix^ z**7wEuhS>#Sfx{^x3yjk7;r&87_Fj`SIDVZ9+jjs7oJZBf4uin)sXX8)2r{;SP&(G;&OVZEXYh#LD+?Bl4N)l>y z6LTf*!; z%57Q-`p>f&Zq~gD31d?WKJh3^i;+41TBC5BUKfu#BQ`c}svmzzTtU}hSakiCyJLm( zwn~rq$6pTDvjd*vJL7Hxa#&xjayMzNGT=R0mDTX{&h+6UsdmAQCon)`RY`SxiOH^1 zGVfraNx*k_->&i?MNEe)72Uh*rG#GQ@A|orz5v(pUW)w$xDA>^%5G2p{I;arxZR!G z4+{>*G~Jq7g^o<_Q2~|>cf8eg9O}9E)x-Id<6ZzI?~sCVKU#Ki+y^S_BOMQ##b9yY zIBBSr&q$JOrX25ejB>3gC9}Lt(mnR(vr8yQ2JyN}2Q2f$`af8qDS6Gw#lFc17q+J) z^}DcowUQk_<$?SqU}17rLl7fjHPu+4o8S5}i*gdLN5j-+mZ4|G{bg32byP1!DpS}e zZTuv>^iN`NK0vyz(mYZs56eYk_WT8?VQX$HYjHB>G5dEFWje-3FMJ5WJfwnsVlZG- zJ<@Tt2gH$Rlp6F0p0N!catjGhlH}xDIq1Aw9{gZHW|k_aLes_eZPU9@rVR&7f1mGa zJXX=G3KOE$G;^kr#;I2FQFW&v?ptAP=`RLlyT_+zaw*zIc1-%);1a(gtTi6J9Cj7| z^=aJ59<)~are+p5dt3O;RL|q^?tb=Hk00rtmY&K-&5JQpG@|;dzVG6AChfQVvS}Nv zcj7eIFq999pjn(fjMT~>i-U8>8hW&ihbrNnV%h$2K^4;t&Av-We0?W=joRhg*WmKQ z24oLXu5ok_#L7Q!O6yZ~=?K zRu~MUC6`5}0UXu3j(2N5zPLX*W5BNh8Y(K+WUTT0oLEddoN+1`$NX~IpQCQFiAK5=SW4SqA#R8ZEI6!V}N)<-acAzeeD{M&Q3j;Q#+4;DL{arI-FCI7-92ovlys#YQ{Z!e{#U zC${maDya(8+bw!AMaD&*`+utXr$x;~dHXl44fhqAw!e_!^ML;pwfo=y_&-iZLPwK- z!2+GWUz|d;kJl$1{3VMG59UEsbNM!;J#x9X*lak)R>c%{J%e9=4T+&YKI&oboNjm5 z4-{)|fYuuERYDvqb&HFahoHHS#&k(u=o6d{fnCnZfv=gjuH761xXD`YbgEwxd1+|R)Yp{mj#pPM=xC{Gboshtks!)66 zAcxmZ>FKD4nG7><;X6YCMJ5q62yAIlf0zZ_NNq{Pvg)^+$X^!+U3;D2I;+}-@E?sk zp657xg@bOEI<;8mckDr)o<+~EqPI$d=DGe7OhWLS$_uQI&Fb0UUxHJ+S$bWMl zgDiL7q9cbNvb}a5BDO|JY(0Jiw+4y9{djs|304&qTODgBS5fS3tlEOwq>oZ@sM@1% zgSrgU_4pV(9oZDV{k5@D;=Rj7N<4Q>n}ggjaZ!{cn21_0&bs&8Z%qXrgneGCK?tBX2k%v)zph?iw;xNC;~1tyoNn96lWYVq zlUf+eWv%L@9iJP`C_r2qB{tq9N}7f?-Yos>XEY!s?^SUk-n*hM#zzd6t8*;Hbe4(I z{5%rE-;?O{(Nf?0d3gY$C-0LrP;8jcvT4!J_bZ?V2y3-Rsd0rF)xL{(aIiqF&zqM{ z8q6b8{*V5fcU>FY6Yhn9(D`=c{E^6y4%CnI>(>V~%;M9I4e=x@l`GM34Z7C=PLM*; zqXCJJCFLloc|y^?lohTd>&inKMZ3`ht4{Y$t0m9=oPe85CLFk(B{WdL7DtLH2g}z$ zYtoHql9#_Zez1ZH3ZNEs6rCBt%>zsox!Ju2!roZ`_u@`C=mgm+_L+YRni#CMm^dw( zW4}dwnWv;KHw;v9 zc2XgH>9F51?>J6s6EyA#(^`CH=R+%J@+~Whx=-Xj*NJ#Jca)8S?px@HS{fTzS}as` z(EH2DGo`Ohdt28lY3BHA&8L%=5_j^Byv!KplQWcm8q=p{k~`aHYfdJ@AWOU!)A_-? z0TXq&!StbHmi?x}0TmiV_fU+oH6hq{&IE5l@a%kcazI>UUE@7i+3^d<7ey5Q-M~7g z>7952r%>6e=P`0iIU&Kk-ay25-MT4PPow-o3o}GK;#rm{!%bvqSZMUKn8J+W^u|Pc>JAPEw}pIOgwmA7qJiC*m`Lf zO6h&xxx9R|D<$n60;-v&JRIwSa2np5%Xe-(M;%J6a>RC*j;Wn8ewv41JtsuWLp*~} z)u+f?)gH>n5D#){Tm6Ns>mz$=_9D;4rQ-SR)Cg@T)rQ7J#QYDJJlliA4j#L#lAa8I zs?wW0y{RfLqtiVI8@WfxCnj_kYu`*ziio{Ypzep5&&|xkGk=1pu<`0>!4Q$Wuks{+ zs;wsfU>!EdsTbsvA@Qf}>>4p?%2CVdTUmSRjXU!DW50xh`@5XYCH7UEGIGB5BVv*d zOw&UHc@Mx8D9AS%ahgRBd2&1LmrHvsDV*Mv4ox&KDjr#6diR9PxWSs1mvD?0kXPf1 zIS7uOD#36AmPJu_C(3;(65kY1!2II+4AN|D*IrHh*Cu7AOX2|1)F1?_UGCk9pb7P0 z`nbDd9%O6SvyHs-Jc+GzU15!Y_e*v1dhv{yJVhI?o7+ocxMDeMkZIV3R%9*i9RdR{pOEXNZ9AU;iLS|-2M_2f`2j`<~aK?BA9P@%Q2}x8uFqWH1g^V zm%4nsWP2$M&0o2PizVR0mAa$#iQ^L0pj4KxzU*)H8_JEFXN<6K(E@pP4t7puK97tS zhJSPyJir;-7z+0fZEnV#U&S>ztsj=H<%+*Wzs9sGcg{LlO*6|71N!W6| zwUdBXe8CNUL8}VBrGw08$h^9s-cj;(0`%w3N>d|q(ep&POqPhiVU9Xek+6qCiG{~W zWVF!nrnT6%?fRIHDVd?=nkF>?N@Z}>d^>;GGV!~b8t&i@x&8s;`cTnG5ZZbFU)6K&X-gwSc3^Pni$btZ}}H+>PC z;r|o)80>UhFM)*`&>nGZ@B-y^)ZSgfW5i6z( z<`62qRVcPLu(F=M7qyaO@tj+(O0Wap59U4@`Y9(9!Jd<|$@qSUu*3!Tq~VYnM}$gQ zrTUDqZ+Y=BzjC^l#V#9ZZv95UlDAE1Ytw;u>y(Q$M<1}AydLzxUkK${=doTi+Yq3m z(Ns-3x`gg<cm3A45h6?~puZ@&dp5V>z_5FukADm@9G}|LlA4udg%c8SG246ps_>m0MA>f}tS}18%!B#*-NxV*Xl@%xZRr^EWh)&` z5Z_lP{VVFnwhT7$~+*% z`(T#ZX@Mi8+^AF8Ye#;D5rY$U!h0S1h&)g`0zzrx9p4Z70r89$vTKBsEW#*gzvMh} zCLr!&;#KU2nPoZ^d+XzR-bMo)UjN<(GuS+H=i~Ia>*V#`^D9=bwy1;-;00Z=jIoUk zvSbM}mDjCyu_`g`M7s628`1|%*4lMByr@-^Ny|}Q(HV^SGya)9x8aXC_%W)^8)r`R zD*wThsra~q!qUjJicsBSO9D38X2u9*`?Q{W-7)R*W;GFOl}Cc%r^Bi7Cc&I>Y66Wz zt5(SuCH8BF61z?2=XOA0mxEkRsT zLOc1_4pWe3z3JO@ams<8Dvn8%Z?u#2<=-21f^D}$%3ym^p|`8s*^MJ3Bz7hzO~o}G2^FeGc8O;4u~*&5laYwMvarb6fKHlP;t zn(1t`1K(+`98W{EC54o@?9*hJb#H&z9jESXZE&N^!;opP3gSjS4`_z}b;~csXy~E) ziapj$#ZFXEl|O{7tO5ixd2Z2%4?6~mAK`>=2zhs1a@^uUOEbGj91|w|=$BWZa5Qjv zy~4Q5g8w3y=JAUq8b{>LQNWsz$(A1ph&;k$e6iCIO^ydK-wZRmAk zps}G=1Hb&85HMqwidboHKZ)_1YhWK1Cp*U`hS7J-9shY^Q00FMBW;D&(3(9GWoXXR zoaD#N3q%&BB~ ziF-z>(rz)#-#r)&YB3>ehhI@gT9_WcDp)|B+N9^AwP_XfDi zs=rO7Y;1O}w+Hw#rn*oR3mDw^29|u$Ha8^y8c2fbj5>`cJZKDLqBm)=mxl%NwxjK- zvtno{d)fe`H7yU>0K4T$3L|i%udEd#gyH_8sMU43Qn?wy40tHgFJkNst>5OzHu)_tlySH|1E_f6>; z1t(GE@YLL4H9VGzQ`1Nl^DG&O6YbcOmEde~qVN|-(}ZG$@tatWY+HgB-5q>50>ltw zv29y1VNNMRmIv5ZcCDDfHRE_U;-1;TeCZ94C>-avj}A1CsJ_LKl- z4f&DmZ)-x!7XaVtsx>?Y+*=$0`9{;@8&=)CK1v1yn02i+YovTkAfgr&B{JY{4H1AB8H9TY>{3Df?KEPE0O^rY<&UVA8qMtJry4QHA{F(Xu`nIcPC79E_3@c0?n6vCF+D~f0O=yULB}PF zd%JV%p}jJIXWagvRowCIlk~29EuBpOttr%dtrn83>jkP=hHspE}jRI-XbWL`=BgzoxzWFL53Rs}FI6D+hT` zWJG`1UNAFsOl{5bvvmXuQrV1rj@-JHCQLPU`LZ~i{aO2cWJ5B(OTYzR+8SG$Y40v; zpaf9gmo|N2r@ahGsn+G|o(mFVtEdbAWdC)3`(Bk!z#vrch{^E}}Vx_L3lgw6*A#G8VSig}*485!{GBnJgb9-p0Cw$-6 zea|fHTE!dq1{D-rKOn*+Cd)i@@}{=a&+tdu8>7!LG8r#hf0`uD%xVzHud`wpDwRAt zKCLj#@Nd>@z^hk(m-8Tg*TsVv^G z*qc@48<=hH=vXv1OG5L7ScoHmfPhl%DFKm;9>u;l)72?f5?e7P_o(Y8({7{cGvZU) zV8M#9eQcUdPfIE37S|XJ;f>8LKS!>pDBYVvmp|2J%y|u^-_K?6i{_eiKzr7lRajAE zBXn_Ft&nThlt`a>{ag7=v8tpTqJvPjZuHrQT_vTXAvpg({5JpS3I9*k4*zdB-+!!c znqipWU|D=jUJ*F$>v#OL5_{kxhDf z+JNTXTH|L06)vt~0;@ybfj$*>FJS+SZ`|1&=Hepg-|#**iT{rG`O+{LbFn13$$CSW zUk&MJX^POSr;TJ?jkb66KB+%U0&aNi8(>}#`u=z3p$0Qe_M5|qoA%2f^PS1#f1mXK z{+~?xLq2h{Pko_@!w|2sLxZLou?8FPRa!HU$;xjMSCCQ9$7si@lOk(8lznIWsb~Lb zIkixQN}#GfFWHtOLZF}wAE}TFp!@?$DXdu?MrRvj9h<7!WT|^6**Vn?LO65+%DQeD zHP&T>P+bz+lg8ZG<;}KP`c-@hRW$SAZVFBsqCi@^2U zn=WU?x{Gcv$O~;nsE>|2=0!^GeN7;@_)D;Vpq`V%QPjdXSw`j{u40E~ayP_kleHF@ z81G9dmj>0}3ytYQF8q9{$X3BkYmd0gRP?bNKM%L)1&LPYaz)fvzq3Hd84n2vnpu4oknAJ%Nl!t6j>%r)p-`HRhc}Euy_b&hQFxk~M5OeLvxXdgC+_J=ws+FyM7+~R8WK~ud2IS*Jp^`Pc)DGLhlT@Cqb(0O z!*8N-Z?rBYNsMVG7+>Nib6OSj%}lx-lepn?hEyL1Hq2C0D9vQK-==ELAP0LX5Eyt~ zW?Sp=8HO=#mxN;l<)Z{3PouVpMH?E^4%GxgQVkJ|d5K0XzITi+=4xIAJZm#ZFjJngp!^*q*REsqpq ztQ9xLFRiGnx-!Q}X;Rx)Q}}kI-{lR1!7Vv`M?3QV6;izUmd4aU_}WVeO?}{=jf7Fn z!;pPzfu`{LbS~kHCmNOBVgT_T)PeJUnpAAeJSY6-$;iV&sLNe7`l}!^JJoj;5;Iyb zvF%Ii7#=Ae-OF>YEh&CQ1PAz8%}*euH?RlCIm6F4)hDZS1NqQo{f_4=j5uTRJh+^0 zHlsWy`3ny;94BCAjUx>r+V>Sm+z2Ae&iA_)S6>HNeD<_rkTF5+Np#aAI>q;2h#vZ) zQVir9l2@;eF4_YC?Xj}Wnw{O9kMVqcw7_s*i(}po#Zs6lLxtaCd;M85v2q{VU&ww& z3AcJ7Nrq6b)koD0@!t$q1ZG6qdGUS~f#Q~?pG^(R)+_H>r>z~}fv&IgNgfcoyeD-{ z(Ff|b-s=S>vP|J`A5mSa9F#|kDH@?u2;YZwlK!NNufbU4?nQN1Z$!+g3 zQfo{F!qvO33j_=+LWzgl{}D;e6Lp~0nu(%k)RZ)Vh6CZ(U_z?&e5`p(4Jt;C zcD7Bn08{b0uGy}F*wobVj*;r+$z2`eQO2(vYqjZ?&tB0=j^E31q#I`fc&<=l0Ogo| zrI2@ZZ8gC}9^UmIsCsLO{C8M*AF;~|%GImCh!I^5lNc&SPL$vm7_^{0IWbdH_a;7( z!N{lpv92QPwB?ziD6oYp_CANB+W?h@9%{K#jt@6jJ8OR^a#^ewp=I+Wdf2&0B*t5Z zhBczpHXmYrjPWz>oaRcBw5n}NT3n%2r&jKQsS?timy7NzK50*}(O8M{AH>=1+&DUG zkRbGCKx!58(`PF05J=y7-S*m_)8?LuMZX%;rd|+jQ=G^Y`v#8~wJY?y+D&xu_^VDF zc09lkuWyCTg4;`X-NJAPe^OOd!LmEfi!TmfVs5V`-1FYT{!w$-drDn58s| zJZQYwbw;12_Do9LraHVo1Pkz3%cHgiv(>16Zkuvk?+kN%a%oxMVM%t2kVR1DbQCBa zjo&8jqgi2{b&r+T7l%GxS~4^dnS|F37)2#BRtVgZWNAb0{eG_vFdKZ6$;Er(A$w;d z;`g8&uV~Rcj&|a7^!3Vf2g(b8_-jQcdMZ`*Vd)@RK5wbk>P04qD3(~)o1`VOOv33mk-G(G5; zN_ks*U&$+o(8CTOZZ`9wttD>2))z1v`p!f`HMI6Aj4u8mF~Li3x}7^&YrDntIY~RF zdcnCsOHsMs(bse$@I{AWYmn*~5j2jeC-`+x05#E-)rv43J0l*j&~3|3qI8V^^l)4H4T-@N|p0n?{QocGl^1T<|?dky?L zR^oaNs&wYAZG^gQsfI1)`#dZ=9BX8{(I#wfMaCS-83Jm+g){%};`T?^wCt2@k zYY5*zAN@={FWkqhN+5ZQHl~`q*pA3BqWPq+4N7s46}-}yU>dmuq<>t)D6~AE^wRLW z3g5qchh-t3wctf2RaN?qsv(lEiVHnD(fLEyfM;5EnC`P@nh{Cm;SOK#;2_7D=nUri zLJZ6tCa0?3x7=&j9sbF+pSkD@u$p%QuXVTR2j*pNf0SSO`Iq2d5D5RKa{tdJys-pk z)ABQ`LGyR;TS3&NanYw0FsrNVqpP)`(~q+hlYuEzR3m?$)8RdG@``5tsBHiZ2vr?h zXg!Sy?CBE>;DTA8#nPY@<`w>vMQ*jKtiK+%26<}!3CTiVTax?O#@yir($|7RX$V68 zCrdXyReDnW=jalmj8A*_0(6cHFcfRitxZ*HRx^UZH%Vi>s^`JqA0>QO zVq|{3BRNCFd#5w0LIe3tgYh6w|*1Ue(*ztKRMq&6|os z4ZwY3Rf-P{b?9fmtLiCY%ShN@Z0t&mJkE03{ZxwQIZbKi5%Tvoe3%f?uPkHfA|HtL z|6+=`b6jm><77T@dTeUlDK2=!4fYpJ!LLxLVee5-o{iOY;vNTaLs^#MPk}G0SI-;M z5%OM`2;IK~pD=bPhHf*~sd@)qtvM{v$6LU|)c9@OjXeTtsft8Iee$0D_Hpyi(yOOp zJO}0`@e2_{G?d>u*F`OlFLIl%g>x*XpR$xftp5@OyIa$n$BC@cTZ7rB9b{e=ba`2L zP-mAbK*i7_x@CzL&bmT(V}Dr@QtfItY_F#8dTf{_1jw(S%809F11E7L2Y(4Ds@!Gi z+Ehxt6Wud5TBbKz>Zd=vpvx?Pru%=$O}AdD3R|LaqCL&PYdx``S$}01S-Mr}fjp_G zENBN1wRrc%(i=E?OB7SZtlmAmOl%M7{VS}{Ne z$+)vnON4)gJ&PB6+-&Z;Us-kMT0Y1#Rb&cK+>y_jG^ZnE4H_OyzM;*(d@FxC&Ja)m z$r2DUX)XRsP}F*J4R&y}9vl;N>VG!`qV?y!7RT`R&b4T!iF%<)|0o}xXf!rc_OhDr z`qW4bJV}SQYra(NBAA09-4G6%=8?YOg!2}4L*R!WL!1E0QAaWdR*Tx5p_9f#!ot%_ zZynu>yvA{?29^!x0J##&#fL-(>*^vl!(sA&;QEX7WE8F=&fiIDWDlA!(d+H(hBv_u z{(zujBQreyNM)Ke!mtUdCdd}E+}BF|Q9P`Rod9t+s;|%`W+U%7XpjEpI-I#g)>;j+ zjs28~dUYqs?dxDy|AF}{>|@k+UOrZKrp;%)RRhT6uR66UkVP_?)WX3U6|3-TQQT7X ztxpi2QjeUonEwS<4~Bg*1HrF0n2DNeTC#=^*Po~4rB)OfkUptQu4K+bBSl_!L%JZc zvV8GAhhD1${#*rEZM4C+hM-3#iW`E|VSY&AZeeu?%~to`R>zDlO?Q*G$|&W$H`?Ym ztYSpR8NH{qXWCRA!fb6TEM_0x%_4Zd81 z2)-L`dt7bo?ezOftjy2CsoV%;+Rt}xpj9*pr2WuN$M` z1!}Chz{XdBL7DVR%<|gCR z6!_cse)@)k`W%tN`?fj0!q%+kT5okke4eB#$%GN!ZiC>6mJ9Z9qNtrX_xMK(paU47 ztx-H-!PdEIghF%BnO-lpOKX#7>&xJ;ov6byfFvNuxbT5e7q@soC)Z`3m^XvAshtTr z6;lWODK5pFFSv+gv)+hz{Zw8yHbIWsJVV@x!sk*x1`z5-T`lr|%3@44hO1T=Y`LUG zDSfiHk_ib3mL!<3)VLmxHh*$~U1*q1aol-$iy+{h4X$^EVM@m6`Zb<8wViHRxwkEy z6l{CktMJ8|OhX<{g zCwASC@$RyGm7|%)6C7^uQ{_JgB)jyVUIi<8KRmCM3v=|P`4vRG zi{Ay8@qp~F@U{cIe)EAYfT*_VfRuyzn2}Ki6N(xd4i>%_o{UfLaJC(7L`0t*(PO5r zE_(5;&-;%yrmtV}n%Ak6^fPPoPqE@E*-A~A1I=6)MN81xH!O&}cz?*UkMHJ4wgk+7 zkxW^N@x+t{`N~5se*>SPHd_Wf8%6Gfh)7ptM`pAokIeOo#Pz4SQn~gg*Zb=G{g(Rz zwTgFHx-GpUaYJge;$3Joa}Q=}JAc9Lu~pT*WFk%1mLIyB_N+0!we>q~5?Su zLd!Q^JH|rt92?-L9|mqOw`m-7vMDo#{YtKa^^+X)*}c43RAA}tT&cXWkI_>mlVlmo z;FhqC>sbfa&Y4rzJK)RXf>60{on!_@j1#7^FYIHhj%z$Q!mq1x++}%r`;zFeAF77j zJy92#e!@&o^A$h(_uX{qlp8p`eIKwAQtXj;>vPgLLZv4Yx%@p7t7OHXMhE*1veeg6 z`B)Sz9jxTe(y-5in*vMSQn>A^!gOwYa+NnSac(~UK`^rOpbVk=zc_jNpOtX^KXsm< Hzl;A5QP||& literal 0 HcmV?d00001 diff --git a/lab2/images/06.jpg b/lab2/images/06.jpg new file mode 100644 index 0000000000000000000000000000000000000000..57e6d8c58b8b963cc93021e5391f0bc7269cb935 GIT binary patch literal 91139 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⪚MREwst*%Y;!tgW-eKvloT&Au>`feJ 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 literal 0 HcmV?d00001 diff --git a/lab2/last_task.cpp b/lab2/last_task.cpp new file mode 100644 index 0000000..2097363 --- /dev/null +++ b/lab2/last_task.cpp @@ -0,0 +1,25 @@ +// +// Created by maxi2 on 17.04.2024. +// + +#include +#include + +bool checkIsSequence(std::vector vec) { + if (vec.size() <= 2) + return false; + int fac = vec[1] / vec[0]; + for (int i = 1; i < vec.size() - 1; ++i) { + if (vec[i] != (vec[i - 1] * fac)) + return false; + } + return true; +} + +int main() { + std::vector vec = {1, 2, 4, 8, 16}; + std::vector vec2 = {5,25,125,625,3125}; + std::cout << "Is sequence: " << checkIsSequence(vec) << std::endl; + std::cout << "Is sequence: " << checkIsSequence(vec2) << std::endl; + return 0; +} \ No newline at end of file diff --git a/lab2/main.ipynb b/lab2/main.ipynb new file mode 100644 index 0000000..856f3dd --- /dev/null +++ b/lab2/main.ipynb @@ -0,0 +1,144 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A. Debugging\n", + "\n", + "In der Datei [taskA.cpp](taskA.cpp) ist ein (fehlerfreies) Programm gegeben.\n", + "\n", + "### Debugging mittels Konsolenausgaben\n", + "\n", + "Die wohl einfachste Methode des Debuggings ist das hinzufügen von *print statements* (std::cout <<) die beim Ausführen Aufschluss über den Verlauf des Programms liefern. In [taskA.cpp](taskA.cpp) in der Funktion `doubleVec` sehen Sie beispielsweise wie diese aussehen könnten.\n", + "\n", + "**Aufgabe 1:** Fuegen Sie in der Funktion `findMax` aequivalente Debug-Ausgaben hinzu (am Anfang der Funktion, in der Schleife und am Ende der Funktion).\n", + "\n", + "**Demonstration 1:** Kompilieren Sie die Datei [taskA.cpp](taskA.cpp), führen Sie das Programm aus und inspizieren Sie die Konsolen Ausgabe. \n", + "\n", + "---\n", + "\n", + "### Debugging mittels Debugger\n", + "\n", + "Eine weitere Methode ist das Verwenden eines Debuggers. Dieser ermöglicht es Haltepunkte (*breakpoints*) zu setzen an denen das Programm angehalten wird und von denen aus man Zeile für Zeile durch das Programm gehen kann. \n", + "\n", + "**Einrichten CMake-basiertes Debuggen mit VSCode:**\n", + "\n", + "- Installieren Sie die `CMake tools`-Erweiterung für VSCode:\n", + "\n", + " ```bash\n", + " code --install-extension ms-vscode.cmake-tools\n", + " ```\n", + "\n", + "- Fügen Sie in der Datei, die Sie debuggen wollen *breakpoints* hinzu:\n", + "\n", + "\n", + "\n", + "- Drücken Sie `F1` und geben Sie in der VSCode Suchleiste `CMake: Configure` ein:\n", + "\n", + "\n", + "\n", + "- Wählen Sie im Menü den `GCC` Compiler aus. Eventuell wird dieser bereits automatisch ausgewählt.\n", + "\n", + "\n", + "\n", + "- Drücken Sie `F1` und geben Sie in der VSCode Suchleiste `CMake: Set Debug Target` ein:\n", + "\n", + "\n", + "\n", + "- Wählen Sie im Menü die Datei aus die Sie debuggen möchten:\n", + "\n", + "\n", + "\n", + "- Drücken Sie wieder `F1` und geben Sie in der VSCode Suchleiste `CMake: Debug` ein:\n", + "\n", + "\n", + "\n", + "**Hinweis:**\n", + " - Das Debuggen mit CMake ist nur möglich wenn eine entsprechend konfigurierte [CMakeLists.txt](CMakeLists.txt) im aktuellen Verzeichnis vorhanden ist.\n", + "\n", + "**Aufgabe 2:** Setzen Sie in der Datei [taskA.cpp](taskA.cpp) dort *breakpoints* wo sich die Debug-Ausgaben aus Aufgabe 1 befinden. Debuggen Sie Ihr Programm indem Sie entweder Zeile fuer Zeile oder von *breakpoint* zu *breakpoint* durch das Programm gehen.\n", + "\n", + "**Demonstration:** Debuggen Sie Ihr Programm und zeigen Sie, dass Sie mit der Debugger-Navigation umgehen koennen." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## B. Klassen\n", + "\n", + "In der Datei [taskB.cpp](taskB.cpp) sind zwei *namespaces* `one` und `two` gegeben. In `namespace one` ist eine Klasse `Particle` mit drei Member-Variablen (Geschwindigkeit in x- und y-Richtung und Masse), welche ein Partikel darstellen soll, gegeben.\n", + "\n", + "**Aufgabe (Details siehe [taskB.cpp](taskB.cpp)):**\n", + " - Erweitern Sie die Klasse `Particle` in `namespace one` um folgende Member-Funktionen: `energy()`, `print()`\n", + " - Erstellen Sie die Klasse `Particle` in `namespace two` mit folgenden Eigenschaften: \n", + " - Gleiche Member-Variablen wie die Klasse in `namespace one` diese sollen aber **privat** sein\n", + " - **Konstruktor** `Particle(...)` mit x- und y-Richtung und Masse als Parameter,\n", + " - **Member Funktionen**: `set()`, `get()`, `energy()`, `print()`\n", + " \n", + " - In der `main` Funktion:\n", + " - Erstellen Sie jeweils ein Objekt der beiden Klassen und rufen Sie alle Funktionen auf.\n", + "\n", + "**Hinweis:**\n", + " - Formel für die kinetische Energie: $E_{kin} = \\frac{1}{2} \\cdot m \\cdot (v_x^2 + v_y^2)$\n", + "\n", + "\n", + "**Demonstration:** Erklären Sie den Unterschied zwischen einem `struct` und einer Klasse (`class`). Was bedeutet `public` bzw. `private` in diesem Zusammenhang?" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## C. Überladen von Operatoren\n", + "\n", + "Sie sollen eine Klasse implementieren um Polynome der Form $p(x) = a_0x^0 + a_1x^1 + a_2x^2 + ...$ darzustellen.\\\n", + "Das Verwenden der Klasse könnte dann in etwa so aussehen:\n", + "\n", + "\n", + "```cpp\n", + "int main(){\n", + " auto poly = Polynom({4, 2, 3}); // this represents the polynomial p(x) = 4 + 2*x + 3*x**2 + ... \n", + " double x = 1.0;\n", + " double eval = poly(x); // using overloaded operator to evaluate polynomial at x = 1\n", + " std::cout << eval << std::endl;\n", + "}\n", + "```\n", + "\n", + "**Aufgabe (Details siehe [taskC.cpp](taskC.cpp)):**\n", + " - Erstellen Sie eine Klasse `Polynomial` mit folgenden Eigenschaften:\n", + " - Member-Variable vom Typ `std::vector`, die die Koeffizienten $a_i$ des Polynoms beinhaltet\n", + " - Ausgabefunktion `print()`\n", + " - Überladener Operator `operator()` zum Evaluieren des Polynoms.\n", + " - Erstellen Sie ein Objekt der Klasse `Polynomial`, rufen sie die `print()` Funktion auf und verwenden Sie den `()` Operator um das Polynom zu evaluieren.\n", + "\n", + "**Demonstration:** Erklaeren Sie Ihre Implementierung kurz. Nennen Sie moegliche Vor- und Nachteile die durch das Ueberladen von Operatoren entstehen." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab2/task.cpp b/lab2/task.cpp new file mode 100644 index 0000000..5df2e9d --- /dev/null +++ b/lab2/task.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +std::vector addSequenz(std::vector a, std::vector b){ + + int shortest = a.size(); + std::vector erg={}; + + if(a.size()>b.size()) { + shortest=b.size(); + } + for (int i = 0 ; i < shortest ; i++){ + double ad = a[i]; + double bd = b[i]; + erg.push_back(ad+bd); + } + + return erg; +}; + +int main(){ + std::vector a = {5,10,20,30}; + std::vector b = {5,3,8}; + //expect 10,13,28 + std::vector c = addSequenz(a,b); + std::cout << c[0] << ',' << c[1] << ',' << c[2] << std::endl; + return 1; +} diff --git a/lab2/taskA.cpp b/lab2/taskA.cpp new file mode 100644 index 0000000..8d4a3ce --- /dev/null +++ b/lab2/taskA.cpp @@ -0,0 +1,62 @@ +/// @file +/// @brief task A +/// compile/run: g++ -g -std=c++20 taskA.cpp -o taskA && ./taskA + +/// @todo add debug statements in function findMax +/// @todo compile and run this file, inspect output +/// @todo runtime debug the program and add breakpoints +/// - at the beginning of each function +/// - inside the loops +/// - at the end of each function + +#include +#include + +// prints a vector to the console +void print(const std::vector& vec) { + std::cout << "[ "; + for (int i = 0; i < vec.size(); ++i) { + std::cout << vec[i] << " "; + } + std::cout << "]" << std::endl; +} + +// takes an input vector and returns a vector where each element is doubled +std::vector doubleVec(std::vector vec) { + std::cout << "'doubleVec' input: "; + print(vec); + + for (int i = 0; i < vec.size(); ++i) { + vec[i] *= 2; + std::cout << " loop(i=" << i << "): " << std::endl; + std::cout << " "; + print(vec); + } + + std::cout << "'doubleVec' output: "; + print(vec); + return vec; +} + +// returns the greatest element of a vector +int findMax(const std::vector& vec) { + int max = vec[0]; + for (int i = 1; i < vec.size(); ++i) { + std::cout << " loop(i=" << i << "): " << std::endl; + std::cout << " current: " << vec[i] << std::endl; + if (vec[i] > max) { + max = vec[i]; + std::cout << " new max: " << max << std::endl; + } + } + return max; +} + +int main() { + + std::vector vec = {3, 7, 12, 9, 15}; + vec = doubleVec(vec); + int max = findMax(vec); + std::cout << "max: " << max << std::endl; + return 0; +} \ No newline at end of file diff --git a/lab2/taskB.cpp b/lab2/taskB.cpp new file mode 100644 index 0000000..741ea4a --- /dev/null +++ b/lab2/taskB.cpp @@ -0,0 +1,76 @@ +/// @file +/// @brief task B +/// g++ -std=c++20 taskB.cpp -o taskB && ./taskB + +#include + +namespace one { + +struct Particle { + double vx; // velocity in x-direction + double vy; // velocity in y-direction + double m; // mass + + /// @todo implement the following member functions: + /// - energy(): returns the kinetic energy of the particle (formula, see main.ipynb) + /// - print(): prints the values of the member variables to the console + double energy() { + return 0.5 * m * (vx * vx + vy * vy); + } + + void print() { + std::cout << "vx: " << vx << ", vy: " << vy << ", m: " << m << std::endl; + } +}; + +} // end namespace one + +namespace two { +/// @todo implement a class with the following properties: +/// - private member variables vx, vy, m +/// - constructor with three parameters (for the three member variables) +/// - set(): function to change the values of all three member variables +/// - get(): returns the values of the member variables in a tuple +/// - energy(): returns the kinetic energy of the particle (formula, see main.ipynb) +/// - print(): prints the values of the member variables to the console + +class Particle { + double vx; // velocity in x-direction + double vy; // velocity in y-direction + double m; // mass + + void set(double vx, double vy, double m) { + this->vx = vx; + this->vy = vy; + this->m = m; + } + + std::tuple get() { + return std::make_tuple(vx, vy, m); + } + +double energy() { + return 0.5 * m * (vx * vx + vy * vy); + } + +void print() { + std::cout << "vx: " << vx << ", vy: " << vy << ", m: " << m << std::endl; + } + +}; + +} // end namespace two + +int main() { + + /// @todo create objects from both classes and use their member functions + one::Particle p1; + p1.vx = 1.0; + p1.vy = 2.0; + p1.m = 3.0; + p1.print(); + std::cout << "energy: " << p1.energy() << std::endl; + + + return 0; +} \ No newline at end of file diff --git a/lab2/taskC.cpp b/lab2/taskC.cpp new file mode 100644 index 0000000..032e138 --- /dev/null +++ b/lab2/taskC.cpp @@ -0,0 +1,44 @@ +/// @file +/// @brief task C +/// g++ -std=c++20 taskC.cpp -o taskC && ./taskC + +#include +#include +#include + +/// @todo implement the class 'Polynomial' with the following properties: +/// - std::vector member variable to store the coefficients of the polynomial +/// - print(): prints the polynomial including the actual coefficients to the console +/// - overloaded '()' operator to evaluate polynomial at 'x' +class Polynomial { + +public: + std::vector coefficients; + void print() { + for (int i = 0; i < coefficients.size(); ++i) { + std::cout << coefficients[i] << "x^" << i; + if (i < coefficients.size() - 1) { + std::cout << " + "; + } + } + std::cout << std::endl; + } + double operator()(double x) { + double result = 0; + for (int i = 0; i < coefficients.size(); ++i) { + result += coefficients[i] * pow(x, i); + } + return result; + } +}; + +int main() { + + /// @todo create an object from your class and use its member function and overloaded operator + Polynomial p; + p.coefficients = {1, 2, 3}; + p.print(); + std::cout << "p(2) = " << p(2) << std::endl; + + return 0; +} \ No newline at end of file diff --git a/lab3/.clang-format b/lab3/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/lab3/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/lab3/.clangd b/lab3/.clangd new file mode 100644 index 0000000..0310bd3 --- /dev/null +++ b/lab3/.clangd @@ -0,0 +1,23 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: No + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/lab3/.gitattributes b/lab3/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab3/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab3/.gitignore b/lab3/.gitignore new file mode 100644 index 0000000..a01b1e5 --- /dev/null +++ b/lab3/.gitignore @@ -0,0 +1,358 @@ +# custom + +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab3/.gitmodules b/lab3/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/lab3/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/lab3/CMakeLists.txt b/lab3/CMakeLists.txt new file mode 100644 index 0000000..2e4b670 --- /dev/null +++ b/lab3/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(lab3 LANGUAGES CXX + DESCRIPTION "lab3" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/lab3") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# global includes + + include_directories(modules) + +# include own targets + + add_executable(taskA taskA.cpp) + add_executable(taskB taskB.cpp) + add_executable(taskC taskC.cpp) diff --git a/lab3/README.md b/lab3/README.md new file mode 100644 index 0000000..81e900c --- /dev/null +++ b/lab3/README.md @@ -0,0 +1,25 @@ +# Labor III: Abgabe (30min) + +**Dieser Teil wird über [TUWEL, Labor III: Abgabe (30min)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse9)** abgewickelt. + +# Labor III: Praxisteil (120min) + +- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**. +- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint. +- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben. +- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben. +- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren. + +--- + +Im heutigen Labor sollen Sie die folgenden drei Aufgabengebiete bearbeiten. + +### A. Klassen mit mehrere Konstruktoren + +### B. Abgeleitete Klassen und dynamische Speicherverwaltung (*new/delete*) + +### C. Generische Funktion/Klassen (*templates*) + +--- + +Details zu den Aufgaben finden Sie in [`main.ipynb`](main.ipynb). \ No newline at end of file diff --git a/lab3/compile_flags.txt b/lab3/compile_flags.txt new file mode 100644 index 0000000..bde723f --- /dev/null +++ b/lab3/compile_flags.txt @@ -0,0 +1 @@ +-Imodules \ No newline at end of file diff --git a/lab3/main.ipynb b/lab3/main.ipynb new file mode 100644 index 0000000..08af589 --- /dev/null +++ b/lab3/main.ipynb @@ -0,0 +1,112 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A. Klassen mit mehrere Konstruktoren\n", + "\n", + "**Aufgabe:**\n", + "\n", + "- Implementieren Sie in eine Klasse `Vector` mit folgenden Eigenschaften (Details siehe [taskA.Vector.hpp](taskA.Vector.hpp)):\n", + " - Eine private Membervariable vom Typ `std::vector`\n", + " - Drei Konstruktoren\n", + " - Eine `const` Memberfunktion `print()`.\n", + "- Nach der Implementierung von `Vector`: Aktivieren Sie in [taskA.cpp](taskA.cpp) alle gekennzeichneten Stellen (die jetzt kompilieren sollten).\n", + "\n", + "**Demonstration:** Kompilieren Sie Ihr Programm, führen Sie es aus, und erklären Sie in eigenen Worten den Vorteil mehrerer Konstruktoren." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## B. Abgeleitete Klassen und dynamische Speicherverwaltung (*new/delete*)\n", + "\n", + "**Aufgabe:**\n", + "\n", + "- Gegeben ist eine abstrakte Schnittstellenklasse `Device` ([taskB.Device.hpp](taskB.Device.hpp))\n", + "- Ebenso gegeben sind folgende Klassen, die die Schnittstelle `Device` implementieren: \n", + " - `WashingMachine` ([taskB.WashingMachine.hpp](taskB.WashingMachine.hpp)) \n", + " - `Train` ([taskB.Train.hpp](taskB.Train.hpp))\n", + " \n", + "- Implementieren Sie eine weitere Klasse `Car` ([taskB.Car.hpp](taskB.Car.hpp)) die ebenfalls die Schnittstelle `Device` implementiert\n", + "- Erweitern Sie die `main`-Funktion in [taskB.cpp](taskB.cpp):\n", + " - Allozieren Sie mit `new` dynamische Instanzen Ihrer Klasse und fügen Sie die Zeiger auf diese Instanzen zum bereits vorhandenen Vektor hinzu.\n", + " - Deallozieren Sie am Ende alle dynamische allozieren Instanzen mit `delete`\n", + "- Speicherlecks:\n", + " - Überprüfen Sie ob Ihr Programmdurchlauf Speicherlecks aufweist, indem Sie mit `-fsanitize=address` kompilieren und das Programm ausführen.\n", + " - Sie können ein Speicherleck provozieren, indem Sie kein `delete` verwenden (also keine Deallokation durchführen).\n", + "\n", + "**Demonstration:** \n", + "- Erklären Sie in eigenen Worten, welchen Vorteil eine Schnittstellenklasse als Basisklasse bietet; welche Einschränkungen hatten Sie z.B. bei der Wahl der Membervariblen für Ihre Klasse `Car`?\n", + "- Kompilieren Sie Ihr Programm mit der Compiler-Option `-fsanitize=address` und führen Sie Ihr Programm aus; provozieren Sie ein Speicherleck und erklären Sie in eigenen Worten das Problem.\n", + "\n", + "**Wichtiger allgemeiner Hinweis:** Explizite dynamische Speicherverwaltung (`new`/`delete`) sollte **immer nur gekapselt** mit **klarer Regelung des Besitzes** der dynamischen Ressourcen erfolgen: also nur innerhalb einer Klasse/Datenstruktur." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**CMake:** Sie können (sofern Ihr Compiler dies unterstützt) die oben erwähnte Option auch an CMake übergeben:\n", + "\n", + "```shell\n", + "cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -D CMAKE_CXX_FLAGS=\"-fsanitize=address\"\n", + "cmake --build build --target taskC\n", + "./build/taskC\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## C. Generische Funktion/Klassen (*templates*)\n", + "\n", + "In der Datei [taskC.one.hpp](taskC.one.hpp) sind im Namensraum `one` zwei Sätze von Funktionsüberladungen `add` und `sum` gegeben.\n", + "\n", + "**Aufgabe 1:**\n", + "\n", + "- Implementieren Sie in [taskC.two.hpp](taskC.two.hpp) im Namensraum `two` zwei Funktions-Templates `add` und `sum`, die Anstelle von `one::add` und `one::sum` verwendet werden können\n", + "- Nach der Implementierung: stellen Sie in [taskC.cpp](taskC.cpp) den verwendeten Namensraum von `one` auf `two` um, und überprüfen Sie die Funktionalität (die sich nicht geändert haben sollte).\n", + "\n", + "**Demonstration 1:** Kompilieren Sie Ihr Programm und führen Sie es aus. Könnten Sie mittels Ihrer zwei Funktions-Templates nun (neben `int` und `double`) beliebige andere Typen addieren und summieren? Welche Voraussetzungen gelten für die Template-Typen bei Ihren Funktionen?\n", + "\n", + "**Aufgabe 2:**\n", + "\n", + "- Kopieren Sie die Datei [taskA.Vector.hpp](taskA.Vector.hpp) als [taskA.VectorT.hpp](taskA.VectorT.hpp):\n", + " - Benennen Sie die Klasse in `VectorT` um\n", + " - **Entfernen Sie den dritten Konstruktor** (den mit dem `std::function` Parameter), da eine Umsetzung erweiterte C++-Kenntnisse erfordern wuerde. \n", + " - Verwandeln Sie die Klasse in ein Klassen-Template, indem Sie den im Vektor abgespeicherten Typ als Template-Typ verwenden:\n", + " ```cpp\n", + " template \n", + " struct VectorT {\n", + " private:\n", + " std::vector data;\n", + " ...\n", + " };\n", + " ```\n", + "\n", + "**Demonstration 2:** Erklären Sie, wie Sie bei der Umwandlung für von `Vector` zu `VectorT` vorgegangen sind." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab3/modules/.clang-format b/lab3/modules/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/lab3/modules/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/lab3/modules/.clangd b/lab3/modules/.clangd new file mode 100644 index 0000000..0c82599 --- /dev/null +++ b/lab3/modules/.clangd @@ -0,0 +1,20 @@ +InlayHints: + Enabled: Yes + ParameterNames: Yes + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\.c, .*\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\.cpp, .*\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/lab3/modules/.gitattributes b/lab3/modules/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab3/modules/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab3/modules/.gitignore b/lab3/modules/.gitignore new file mode 100644 index 0000000..093174d --- /dev/null +++ b/lab3/modules/.gitignore @@ -0,0 +1,358 @@ +# custom +install +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab3/modules/CMakeLists.txt b/lab3/modules/CMakeLists.txt new file mode 100644 index 0000000..928f625 --- /dev/null +++ b/lab3/modules/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.28) + +# define project metadata + + project(iue-modules LANGUAGES CXX C + DESCRIPTION "modules" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/modules") + +# setting required language standards + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED True) + set(CMAKE_C_EXTENSIONS OFF) + +# misc settings + + # avoid ctest-dashboard spcific targets + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# options + + option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + option(BUILD_TESTING "enable testing with ctest" ON) + +# external dependencies + + #find_package(OpenMP COMPONENTS C CXX REQUIRED) + +# testing + + include(CTest) + +# include modules in order of dependence + + add_subdirectory(iue-other) + add_subdirectory(iue-io) + add_subdirectory(iue-po) + add_subdirectory(iue-num) + add_subdirectory(iue-rnd) + add_subdirectory(iue-svg) + +# packaging for consuming an install-tree via find_package + + include(CMakePackageConfigHelpers) + + configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/iue-modules-config.in + ${CMAKE_CURRENT_BINARY_DIR}/iue-modules-config.cmake + INSTALL_DESTINATION "./" + ) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/iue-modules-config.cmake" + DESTINATION "./" + ) diff --git a/lab3/modules/CMakePresets.json b/lab3/modules/CMakePresets.json new file mode 100644 index 0000000..8046915 --- /dev/null +++ b/lab3/modules/CMakePresets.json @@ -0,0 +1,30 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "gcc-debug", + "displayName": "gcc-debug", + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/build", + "hidden": false, + "cacheVariables": { + "CMAKE_C_FLAGS": "-g -O0 -fno-omit-frame-pointer -fsanitize=address", + "CMAKE_CXX_FLAGS": "-g -O0 -fno-omit-frame-pointer -fsanitize=address", + "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address" + } + }, + { + "name": "msvc-debug", + "displayName": "msvc-debug", + "generator": "Visual Studio 17 2022", + "binaryDir": "${sourceDir}/build", + "hidden": false, + "cacheVariables": { + "CMAKE_C_FLAGS": "-W4 -WX", + "CMAKE_CXX_FLAGS": "-W4 -WX", + "CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS": "ON" + } + } + ] +} \ No newline at end of file diff --git a/lab3/modules/README.md b/lab3/modules/README.md new file mode 100644 index 0000000..b3e46c3 --- /dev/null +++ b/lab3/modules/README.md @@ -0,0 +1,94 @@ +# Helper libraries + +## Information for students + +- you do **not** need to copy any files from this repository "by-hand" +- you do **not** need to clone this repo explicitly +- you will make use of functions provided by these libraries in the exercises +- these libraries will be configured as a *git submodule* in the respective exercises, this is why you need to clone with `git clone --recursive ...` +- tentative: we will use these libraries to showcase differences when implementing the same functionality in C and C++ + +## Overview + +``` +├── iue-io # reading and writing csv-files of numeric data, header-only +│   ├── ccsv.h +│   ├── ccsv.test.c +│   ├── csv.hpp +│   └── csv.test.cpp +├── iue-po # minimalistic support for parsing command line options, header-only +│   ├── cpo.h +│   ├── cpo.test.c +│   ├── po.hpp +│   └── po.test.cpp +├── iue-num # helper functions for numeric calculations with floating point values, header-only +│   ├── numerics.hpp +│   └── numerics.test.cpp +├── iue-rnd # helper functions for generating random instances of values and generic objects, header-only +│   ├── random.hpp +│   └── random.test.cpp +├── iue-svg # minimal svg-generator from geometric primitives (e.g. box/circle/triangle), header-only +│   ├── tags.hpp +│   ├── tags.test.cpp +│   ├── render.hpp +│   ├── render.test.cpp +│   ├── tree.hpp +│   └── tree.test.cpp +└── README.md +``` + + +## config/build/test/install + +```shell +# cmake -S . -B build +cmake --preset gcc-debug +cmake --build build +ctest --test-dir build --verbose +cmake --install build --prefix ./install +rm -rf build/ install/ # cleanup +``` + +## fresh integration into an exercise + +```shell +git submodule add -b main https://sgit.iue.tuwien.ac.at/360050/modules ./modules +git add modules +git commit -m "added modules as git-submodule" +``` + +## pull for a submodule enabled exercise + +```shell +cd exerciseX +git pull --recurse-submodules +``` + +## bump to latest modules commit for an exercise + +```shell +cd exerciseX +git submodule update --remote --merge modules +git add modules +git commit -m "bump modules to latest commit" +``` + +## intended incorporation/usecase in an exercise + +```cpp +// task1.main.cpp +#include "iue-io/csv.hpp" +//... +``` + +```cpp +// task1.main.c +#include "iue-io/ccsv.h" +//... +``` + +``` +mkdir build +g++ -Imodules -std=c++20 task1.main.cpp -o build/task1_cpp && ./build/task1_cpp +gcc -Imodules -std=c11 task1.main.c -o build/task1_c && ./build/task1_c +``` \ No newline at end of file diff --git a/lab3/modules/cmake/iue-modules-config.in b/lab3/modules/cmake/iue-modules-config.in new file mode 100644 index 0000000..116f8d0 --- /dev/null +++ b/lab3/modules/cmake/iue-modules-config.in @@ -0,0 +1,13 @@ +@PACKAGE_INIT@ + +#include(CMakeFindDependencyMacro) +#find_dependency(OpenMP) # will use spec when from find_package used in this project + +include("${CMAKE_CURRENT_LIST_DIR}/iue-other/iue-other-export.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/iue-io/iue-io-export.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/iue-po/iue-po-export.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/iue-num/iue-num-export.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/iue-rnd/iue-rnd-export.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/iue-svg/iue-svg-export.cmake") + +check_required_components(iue-modules) diff --git a/lab3/modules/compile_flags.txt b/lab3/modules/compile_flags.txt new file mode 100644 index 0000000..718e50f --- /dev/null +++ b/lab3/modules/compile_flags.txt @@ -0,0 +1 @@ +-Imodules diff --git a/lab3/modules/iue-io/CMakeLists.txt b/lab3/modules/iue-io/CMakeLists.txt new file mode 100644 index 0000000..65125d6 --- /dev/null +++ b/lab3/modules/iue-io/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.28) + +# define CXX interface library + + # define target & alias (i.e. consuming projects can experience namespaces) + add_library(iue-io_csv INTERFACE) + target_link_libraries(iue-io_csv INTERFACE iue-other_honly) + #target_link_libraries(iue-io_csv INTERFACE OpenMP::OpenMP_CXX) + + # define sources of target + target_sources(iue-io_csv + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES csv.hpp) + + # define installation step + install(TARGETS iue-io_csv EXPORT iue-io-export + FILE_SET public_headers DESTINATION "./") + + # define test +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-io_csv.test csv.test.cpp) + target_link_libraries(iue-io_csv.test iue-io_csv) + add_test(NAME iue-io_csv.test COMMAND iue-io_csv.test) +endif() + +# define C interface library + + # define target & alias (i.e. consuming projects can experience namespaces) + add_library(iue-io_ccsv INTERFACE) + + # define sources of target + target_sources(iue-io_ccsv + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ccsv.h) + + # define installation step + install(TARGETS iue-io_ccsv EXPORT iue-io-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-io_ccsv.test ccsv.test.c) + target_link_libraries(iue-io_ccsv.test iue-io_ccsv) + add_test(NAME iue-io_ccsv.test COMMAND iue-io_ccsv.test) +endif() + +# export targets for consuming cmake projects + + # generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets + install(EXPORT iue-io-export DESTINATION "iue-io" ) + + # allow dependend projects consuming the "build-tree" to use all targets + export(EXPORT iue-io-export ) diff --git a/lab3/modules/iue-io/ccsv.h b/lab3/modules/iue-io/ccsv.h new file mode 100644 index 0000000..fbf1673 --- /dev/null +++ b/lab3/modules/iue-io/ccsv.h @@ -0,0 +1,143 @@ +#pragma once + +#include // assert +#include // NAN +#include // size_t +#include // FILE|snprintf +#include // EXIT_FAILURE|EXIT_SUCCESS +#include // strtok|strdup|memcpy + +/// @brief Set of values in a row +struct Row { + double* values; ///< pointer to row value array + size_t n; ///< length of array 'value' +}; + +/// @brief Set of rows +struct Table { + struct Row* rows; ///< pointer to array of rows + size_t n; ///< length of array 'rows' +}; + +static void table_clear(struct Table* table) { + for (size_t i = 0; i != table->n; ++i) { + free(table->rows[i].values); + } + free(table->rows); + table->rows = NULL; + table->n = 0; +} + +static void table_append_copy(struct Table* table, const double* data, size_t n) { + assert(table != NULL); + table->rows = realloc(table->rows, sizeof(struct Row) * ++table->n); + double* ptr = malloc(sizeof(double) * n); + memcpy(ptr, data, sizeof(double) * n); + table->rows[table->n - 1].values = ptr; + table->rows[table->n - 1].n = n; +} + +static void table_append_move(struct Table* table, const struct Row* row) { + assert(table != NULL); + table->rows = realloc(table->rows, sizeof(struct Row) * ++table->n); + table->rows[table->n - 1] = *row; +} + +/// @brief Deserialize a row from a string +static void row_serialize(const struct Row* row, FILE* stream, char del) { + for (size_t r = 0; r != row->n - 1; ++r) { + fprintf(stream, "%.18e%c", row->values[r], del); + } + fprintf(stream, "%.18e", row->values[row->n - 1]); // avoid trailing return + fprintf(stream, "\n"); +} + +/// @brief Writes a numeric values to a file using a csv-format +/// @param filepath file to be written including the desired extension +/// @param table table with the rows to be written +/// @param del delimiter between individual values +/// @param header informative comment in the output file +/// @param comments character signalling that a line is a comment (if used as first character of the line ) +static int iueio_savetxt(const char* filepath, const struct Table* table, char del, const char* header, char comments) { + + // check if data is present + if (table->n == 0) + return EXIT_SUCCESS; + + // open file + FILE* stream = fopen(filepath, "w"); + if (stream == NULL) + return EXIT_FAILURE; + + // write header lines + if (header[0] != '\0') { + char* tmp = malloc((strlen(header) + 1) * sizeof(char)); + strcpy(tmp, header); + char* pos = strtok(tmp, "\n"); + while (pos != NULL) { + fprintf(stream, "%c %s%c\n", comments, pos, del); + pos = strtok(NULL, "\n"); + } + free(tmp); + } + + // write data + for (size_t i = 0; i != table->n; ++i) { + row_serialize(&table->rows[i], stream, del); + } + + fclose(stream); + return EXIT_SUCCESS; +} + +/// @brief Serializes a Row from a stream +static int row_deserialize(struct Row* row, const char* line, char del) { + assert(row->n == 0); + char format[32]; + // snprintf(format, sizeof(format) / sizeof(char), "%%lf %c", del); + double value = NAN; + const char* iter = line; + int n = 0; + while (sscanf(iter, "%lf %n", &value, &n) != EOF) { + iter += n; + row->values = realloc(row->values, sizeof(double) * ++row->n); + row->values[row->n - 1] = value; + char format[16]; + snprintf(format, sizeof(format) / sizeof(char), " %%*[%c] %%n", del); + sscanf(iter, format, &n); + iter += n; + } + return EXIT_SUCCESS; +} + +/// @brief Read numeric values stored in a csv-format +/// @param filepath input file containing the numeric values +/// @param table table to be read into (must be empty) +/// @param del delimiter between individual values in a row +/// @return integral values signalling success or failure +static int iueio_loadtxt(const char* filepath, struct Table* table, char del, char comment) { + + // check if table is empty + assert(table->n == 0); + + // open file + FILE* stream = fopen(filepath, "r"); + if (stream == NULL) + return EXIT_FAILURE; + + // read from stream row-by-row (fixed size buffer) + static char line[1024]; + + // read each line into a row + while (fgets(&line[0], sizeof(line) / sizeof(char), stream)) { + if (line[0] == comment) + continue; + + struct Row row = {NULL, 0}; + int n = row_deserialize(&row, line, del); + table_append_move(table, &row); + } + + fclose(stream); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/modules/iue-io/ccsv.test.c b/lab3/modules/iue-io/ccsv.test.c new file mode 100644 index 0000000..86f65bd --- /dev/null +++ b/lab3/modules/iue-io/ccsv.test.c @@ -0,0 +1,63 @@ +#include "iue-io/ccsv.h" + +#include // assert +#include // size_t + +int main(void) { + + // prep input data + + struct Table table1 = {NULL, 0}; + { + double data[3] = {1, 2, 3}; + table_append_copy(&table1, data, 3); + } + { + double data[4] = {10, 20, 30, 40}; + table_append_copy(&table1, data, 4); + } + { + double data[4] = {1e3, 2e3, 3e3, 4e3}; + table_append_copy(&table1, data, 4); + } + + { // write + + int res = iueio_savetxt("ccsv_test.csv", &table1, ';', "this is a multiline \n header comment", '#'); + + if (res != EXIT_SUCCESS) + return EXIT_FAILURE; + } + + struct Table table2 = {NULL, 0}; + + { // read + + int res = iueio_loadtxt("ccsv_test.csv", &table2, ';', '#'); + + if (res != EXIT_SUCCESS) + return EXIT_FAILURE; + + for (size_t i = 0; i != table2.n; ++i) { + for (size_t j = 0; j != table2.rows[i].n; ++j) + printf("%lf ", table2.rows[i].values[j]); + printf("\n"); + } + } + + // compare + + assert(table1.n == table2.n); + + for (size_t r = 0; r != table1.n; ++r) { + assert(table1.rows[r].n == table2.rows[r].n); + for (size_t c = 0; c != table1.rows[r].n; ++c) { + assert(table1.rows[r].values[c] == table2.rows[r].values[c]); + } + } + + table_clear(&table1); + table_clear(&table2); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/modules/iue-io/csv.hpp b/lab3/modules/iue-io/csv.hpp new file mode 100644 index 0000000..84beeee --- /dev/null +++ b/lab3/modules/iue-io/csv.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include // std::filesystem::path +#include // std::ofstream|ifstream +#include // std::scientifc|setprecision +#include // std::cout|endl +#include // std::numeric_limits +#include // std::istringstream +#include // std::string +#include // std::vector + +namespace iue::io { + +/// @brief Writes a numeric values to a file using a csv-format +/// @param filepath file to be written including the desired extension +/// @param table table with the rows to be written +/// @param del delimiter between individual values +/// @param header informative comment in the output file +/// @param comments character signalling that a line is a comment (if used as first character of the line ) +inline void savetxt(std::filesystem::path filepath, const std::vector>& table, char del = ' ', + std::string header = "", char comments = '#') { + + // setup row writer callable + auto serialize = [&del](const std::vector& row) -> std::string { + std::ostringstream oss; + for (auto iter = row.begin(); iter != std::prev(row.end()); ++iter) + oss << std::scientific << std::setprecision(18) << *iter << del; + oss << std::scientific << std::setprecision(18) << *std::prev(row.end()); // avoid trailing return + oss << std::endl; + return oss.str(); + }; + + // check if data is present + if (table.empty()) + return; + + // open file + std::ofstream stream; + stream.exceptions(std::ifstream::badbit); + stream.open(filepath); + + // write header lines + if (!header.empty()) { + std::string line; + std::istringstream iss(header); + while (std::getline(iss, line)) { + stream << comments << ' ' << line << std::endl; + } + } + + // write data + for (const auto& row : table) + stream << serialize(row); +} + +/// @brief Read numeric values stored in a csv-format +/// @param filepath input file containing the numeric values +/// @param del delimiter between individual values +/// @param comments character signalling that a line is a comment (if used as first character of the line ) +/// @return table which was read +inline std::vector> loadtxt(std::filesystem::path filepath, char del = ' ', char comments = '#') { + + // setup row parser callable + auto deserialize = [&del](const std::string& line) -> std::vector { + std::vector res; + std::stringstream stream(line); + std::string item; + while (std::getline(stream, item, del)) { + std::stringstream ss(item); + ss.exceptions(std::ios::failbit); + auto value = std::numeric_limits::quiet_NaN(); + ss >> value; + res.push_back(value); + } + return res; + }; + + // setup table + std::vector> table; + + // open file + std::ifstream stream; + stream.exceptions(std::ifstream::badbit); + stream.open(filepath); + + // read from stream row-by-row + std::string line; + + // read line to row vectors + while (std::getline(stream, line)) { + if (line.front() == comments) + continue; + table.push_back(deserialize(line)); + } + + return table; +} + +} // namespace iue::io \ No newline at end of file diff --git a/lab3/modules/iue-io/csv.test.cpp b/lab3/modules/iue-io/csv.test.cpp new file mode 100644 index 0000000..ea1d1e8 --- /dev/null +++ b/lab3/modules/iue-io/csv.test.cpp @@ -0,0 +1,73 @@ +#include "iue-io/csv.hpp" + +#include // assert +#include // std::size_t +#include // std::cout|endl +#include +#include // std::vector + +int main() { + + // prep input data + + std::vector> table1; + { + auto row = std::vector{1, 2, 3}; + table1.push_back(row); + } + { + auto row = std::vector{10, 20, 30, 40}; + table1.push_back(row); + } + { + auto row = std::vector{1e3, 2e3, 3e3, 4e3}; + table1.push_back(row); + } + + // write + + try { + + for (const auto& row : table1) { + for (const auto& value : row) + std::cout << value << " "; + std::cout << std::endl; + } + + iue::io::savetxt("csv_test.csv", table1, ';', "this is a multiline \n header comment"); + + } catch (const std::exception& e) { + std::cout << e.what(); + } + + std::vector> table2; + + // read + + try { + + table2 = iue::io::loadtxt("csv_test.csv", ';'); + + for (const auto& row : table2) { + for (const auto& value : row) + std::cout << value << " "; + std::cout << std::endl; + } + + } catch (const std::exception& e) { + std::cout << e.what(); + } + + // compare + + assert(table1.size() == table2.size()); + + for (size_t r = 0; r != table1.size(); ++r) { + assert(table1[r].size() == table2[r].size()); + for (size_t c = 0; c != table1[r].size(); ++c) { + assert(table1[r][c] == table2[r][c]); + } + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/modules/iue-num/CMakeLists.txt b/lab3/modules/iue-num/CMakeLists.txt new file mode 100644 index 0000000..827d82a --- /dev/null +++ b/lab3/modules/iue-num/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.28) + +# define CXX interface library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-num_num INTERFACE) + + # define sources of target + target_sources(iue-num_num + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES numerics.hpp) + + # define installation step + install(TARGETS iue-num_num EXPORT iue-num-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-num_num.test numerics.test.cpp) + target_link_libraries(iue-num_num.test iue-num_num) + add_test(NAME iue-num_num.test COMMAND iue-num_num.test) +endif() + +# export targets for consuming cmake projects + + # generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets + install(EXPORT iue-num-export DESTINATION "iue-num" ) + + # allow dependend projects consuming the "build-tree" to use all targets + export(EXPORT iue-num-export) diff --git a/lab3/modules/iue-num/numerics.hpp b/lab3/modules/iue-num/numerics.hpp new file mode 100644 index 0000000..559828e --- /dev/null +++ b/lab3/modules/iue-num/numerics.hpp @@ -0,0 +1,26 @@ +/// @file +/// @brief Supporting functionality for numeric calculations with floating point values + +#pragma once + +#include // std::abs, std::max, std::min + +namespace iue::num { + +/// @brief Checks if two floating point values are close to each other +/// @param a First value +/// @param b Second value +/// @param tabs Tolerance for the absolute difference: +/// abs(a - b) < tabs +/// @param trel Tolerance for the relative error (using the larger absolute value as reference): +/// abs(a b) < trel * max(abs(a),abs(b)) +/// @return true if the stronger of the two tolerances is met, false otherwise +inline bool isclose(double a, double b, double tabs = 0.0, double trel = 1e-9) { + if (a == b) // for infinite values (which should be considered close to themselves) + return true; + auto abs_diff = std::abs(a - b); // absolte difference + auto trel_ref = std::max(std::abs(a), std::abs(b)); // reference for the relative threshold + return abs_diff < std::max(tabs, trel * trel_ref); +} + +} // namespace iue::num diff --git a/lab3/modules/iue-num/numerics.test.cpp b/lab3/modules/iue-num/numerics.test.cpp new file mode 100644 index 0000000..62b65a0 --- /dev/null +++ b/lab3/modules/iue-num/numerics.test.cpp @@ -0,0 +1,69 @@ +/// @file +/// @brief Test for iue::num::islcose + +#include "iue-num/numerics.hpp" // iue::num::islcose + +#include // assert +#include // EXIT_SUCCESS +#include // std::numeric_limits + +int main() { + + const double eps = std::numeric_limits::epsilon(); + const double inf = std::numeric_limits::infinity(); + const double nan = std::numeric_limits::quiet_NaN(); + + { // infinity + + assert(iue::num::isclose(inf, inf, 0.0, 0.0)); + assert(iue::num::isclose(-inf, -inf, 0.0, 0.0)); + assert(!iue::num::isclose(-inf, inf, 0.0, 0.0)); + assert(!iue::num::isclose(inf, -inf, 0.0, 0.0)); + } + + { // nan + assert(!iue::num::isclose(nan, nan, inf, inf)); + } + + { // around 1.0 + double a = 1.0; + double b = 1.0 + eps; + + // rel error + assert(iue::num::isclose(a, b, 0.0, 2 * eps)); + assert(!iue::num::isclose(a, b, 0.0, 0.0)); + + // abs error + assert(iue::num::isclose(a, b, 2 * eps, 0.0)); + assert(!iue::num::isclose(a, b, 0.0, 0.0)); + } + + { // around 1e10 + double a = 1.0 * 1e10; + double b = (1.0 + eps) * 1e10; + + // rel error + assert(iue::num::isclose(a, b, 0.0, 2 * eps)); + assert(!iue::num::isclose(a, b, 0.0, 0.0)); + + // abs error + assert(iue::num::isclose(a, b, 1e10 * 2 * eps, 0.0)); + assert(!iue::num::isclose(a, b, 1e9 * 2 * eps, 0.0)); + } + + { // around 1e-10 + + double a = 1.0 * 1e-10; + double b = (1.0 + eps) * 1e-10; + + // rel error + assert(iue::num::isclose(a, b, 0.0, 2 * eps)); + assert(!iue::num::isclose(a, b, 0.0, 0.0)); + + // abs error + assert(iue::num::isclose(a, b, 1e-10 * 2 * eps, 0.0)); + assert(!iue::num::isclose(a, b, 1e-11 * 2 * eps, 0.0)); + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/modules/iue-other/CMakeLists.txt b/lab3/modules/iue-other/CMakeLists.txt new file mode 100644 index 0000000..faf6d66 --- /dev/null +++ b/lab3/modules/iue-other/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.28) + +# define CXX interface library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-other_honly INTERFACE) + + # define sources of target + target_sources(iue-other_honly + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES func.hpp func.detail.hpp) + + # define installation step + install(TARGETS iue-other_honly EXPORT iue-other-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-other_honly.test func.test.cpp) + target_link_libraries(iue-other_honly.test iue-other_honly) + add_test(NAME iue-other_honly.test COMMAND iue-other_honly.test) +endif() + +# define CXX normal library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-other_lib) + + # define sources of target + target_sources(iue-other_lib + PRIVATE library.cpp + PUBLIC FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES library.hpp) + + # define installation step + install(TARGETS iue-other_lib EXPORT iue-other-export + FILE_SET public_headers DESTINATION "./" + LIBRARY DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-other_lib.test library.test.cpp) + target_link_libraries(iue-other_lib.test iue-other_lib) + add_test(NAME iue-other_lib.test COMMAND iue-other_lib.test) +endif() + +# export targets for consuming cmake projects + + # generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets + install(EXPORT iue-other-export DESTINATION "iue-other" ) + + # allow dependend projects consuming the "build-tree" to use all targets + export(EXPORT iue-other-export) diff --git a/lab3/modules/iue-other/func.detail.hpp b/lab3/modules/iue-other/func.detail.hpp new file mode 100644 index 0000000..fb3ff4b --- /dev/null +++ b/lab3/modules/iue-other/func.detail.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace iue::other::detail { + +inline int calc(int a) { return a * 2; } + +} // namespace iue::other::detail diff --git a/lab3/modules/iue-other/func.hpp b/lab3/modules/iue-other/func.hpp new file mode 100644 index 0000000..c2268e3 --- /dev/null +++ b/lab3/modules/iue-other/func.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "func.detail.hpp" + +namespace iue::other { + +inline int header_only(int a) { return detail::calc(a); } + +} // namespace iue::other diff --git a/lab3/modules/iue-other/func.test.cpp b/lab3/modules/iue-other/func.test.cpp new file mode 100644 index 0000000..c25ff8a --- /dev/null +++ b/lab3/modules/iue-other/func.test.cpp @@ -0,0 +1,9 @@ + +#include "iue-other/func.hpp" + +#include // assert + +int main() { + assert(iue::other::header_only(5) == 10); + return 0; +} \ No newline at end of file diff --git a/lab3/modules/iue-other/library.cpp b/lab3/modules/iue-other/library.cpp new file mode 100644 index 0000000..7d4a3e0 --- /dev/null +++ b/lab3/modules/iue-other/library.cpp @@ -0,0 +1,9 @@ +#include "library.hpp" + +namespace iue::other { + +int Widget::calc() const { return 5; } + +int calc(const Widget& w) { return w.calc(); } + +} // namespace iue::other \ No newline at end of file diff --git a/lab3/modules/iue-other/library.hpp b/lab3/modules/iue-other/library.hpp new file mode 100644 index 0000000..3b81f35 --- /dev/null +++ b/lab3/modules/iue-other/library.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace iue::other { + +struct Widget { + int calc() const; +}; + +int calc(const Widget& w); + +} // namespace iue::other \ No newline at end of file diff --git a/lab3/modules/iue-other/library.test.cpp b/lab3/modules/iue-other/library.test.cpp new file mode 100644 index 0000000..59ebcf2 --- /dev/null +++ b/lab3/modules/iue-other/library.test.cpp @@ -0,0 +1,12 @@ +#include "iue-other/library.hpp" + +#include // assert + +int main() { + + auto w = iue::other::Widget(); + assert(w.calc() == 5); + assert(iue::other::calc(w) == 5); + + return 0; +} \ No newline at end of file diff --git a/lab3/modules/iue-po/CMakeLists.txt b/lab3/modules/iue-po/CMakeLists.txt new file mode 100644 index 0000000..ee37630 --- /dev/null +++ b/lab3/modules/iue-po/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.28) + +# define CXX interface library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-po_po INTERFACE) + + # define sources of target + target_sources(iue-po_po + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES po.hpp) + + # define installation step + install(TARGETS iue-po_po EXPORT iue-po-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-po_po.test po.test.cpp) + target_link_libraries(iue-po_po.test iue-po_po) + add_test(NAME iue-po_po.test COMMAND iue-po_po.test --option1 100 -option2 o2) +endif() + +# define C interface library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-po_cpo INTERFACE) + + # define sources of target + target_sources(iue-po_cpo + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES cpo.h) + + # define installation step + install(TARGETS iue-po_cpo EXPORT iue-po-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-po_cpo.test cpo.test.c) + target_link_libraries(iue-po_cpo.test iue-po_cpo) + add_test(NAME iue-po_cpo.test COMMAND iue-po_cpo.test --option1 100 -option2 o2) +endif() + +# export targets for consuming cmake projects + + # generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets + install(EXPORT iue-po-export DESTINATION "iue-po" ) + + # allow dependend projects consuming the "build-tree" to use all targets + export(EXPORT iue-po-export) diff --git a/lab3/modules/iue-po/cpo.h b/lab3/modules/iue-po/cpo.h new file mode 100644 index 0000000..9215741 --- /dev/null +++ b/lab3/modules/iue-po/cpo.h @@ -0,0 +1,104 @@ +#pragma once + +#include // bool +#include // snprintf|stderr +#include // EXIT_FAILURE|EXIT_SUCCESS +#include // strncmp|strlen + +/// @brief internal function +static bool iuepo_find_option(const char* option, const char* arg) { + char buffer[256]; + snprintf(buffer, sizeof(buffer) / sizeof(char), "%s%s", "-", option); + if (strncmp(buffer, arg, strlen(buffer)) == 0) + return true; + snprintf(buffer, sizeof(buffer) / sizeof(char), "%s%s", "--", option); + if (strncmp(buffer, arg, strlen(buffer)) == 0) + return true; + return false; +} + +/// @brief internal function +static int iuepo_count_if(const char* option, char** begin, char** end) { + int res = 0; + for (char** iter = begin; iter != end; ++iter) { + if (iuepo_find_option(option, *iter)) + ++res; + } + return res; +} + +/// @brief internal function +static char** iuepo_find_if(const char* option, char** begin, char** end) { + int res = 0; + for (char** iter = begin; iter != end; ++iter) { + if (iuepo_find_option(option, *iter)) + return iter; + } + return end; +} + +/// @todo Improve documentation of this enum +enum iuepo_option { IUEPO_REQUIRED = 0, IUEPO_OPTIONAL }; + +/// @todo Improve documentation of this function +/// @brief Optain a string option from command line arguments +static int iuepo_get_string(int argc, char* argv[], const char* option, char* default_value, int N, + enum iuepo_option op) { + + int count = iuepo_count_if(option, argv + 1, argv + argc); + + if (count == 0) { + if (op == IUEPO_REQUIRED) { + fprintf(stderr, "required option not provided: -/-- %s\n", option); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + + if (count > 1) { + fprintf(stderr, "option not unique: -/-- %s (count = %i )\n", option, count); + return EXIT_FAILURE; + } + + char** iter = iuepo_find_if(option, argv + 1, argv + argc); + + ++iter; + + if (iter == argv + argc) { + fprintf(stderr, "option not followed by value: -/-- %s\n", option); + return EXIT_FAILURE; + } + + if (strncmp("-", *iter, 1) == 0) { + fprintf(stderr, "option -/-- %s not followed by value but another option -/-- %s\n", option, *iter); + return EXIT_FAILURE; + } + + if (snprintf(default_value, N, "%s", *iter) >= N) { + fprintf(stderr, "provided buffer size %i for option -/-- %s not sufficient\n", N, option); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/// @todo Improve documentation of this function +/// @brief Wrapping iuepo_get_string with a subsequent conversion +static int iuepo_get_int(int argc, char* argv[], const char* option, int* default_value, enum iuepo_option op) { + + char default_value_string[256] = "\0"; + snprintf(default_value_string, sizeof(default_value_string), "%i", *default_value); + + if (iuepo_get_string(argc, argv, option, default_value_string, sizeof(default_value_string), op) == EXIT_FAILURE) { + return EXIT_FAILURE; + } + + int res = sscanf(default_value_string, "%i", default_value); + + if (res != 1) { + fprintf(stderr, "value for option -/-- %s could not be converted\n", option); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/modules/iue-po/cpo.test.c b/lab3/modules/iue-po/cpo.test.c new file mode 100644 index 0000000..be941f3 --- /dev/null +++ b/lab3/modules/iue-po/cpo.test.c @@ -0,0 +1,32 @@ + +#include "iue-po/cpo.h" + +#include // assert +#include // printf +#include +#include // strcmp + +int main(int argc, char* argv[]) { + + int value1 = 1; + if (iuepo_get_int(argc, argv, "option1", &value1, IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char value2[256] = "default2"; + if (iuepo_get_string(argc, argv, "option2", value2, sizeof(value2), IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char value3[256] = "default3"; + if (iuepo_get_string(argc, argv, "option3", value3, sizeof(value3), IUEPO_OPTIONAL) != EXIT_SUCCESS) + return EXIT_FAILURE; + + printf("'%i'\n", value1); + printf("'%s'\n", value2); + printf("'%s'\n", value3); + + assert(value1 == 100); + assert(strcmp(value2, "o2") == 0); + assert(strcmp(value3, "default3") == 0); + + return EXIT_SUCCESS; +} diff --git a/lab3/modules/iue-po/po.hpp b/lab3/modules/iue-po/po.hpp new file mode 100644 index 0000000..7de88d5 --- /dev/null +++ b/lab3/modules/iue-po/po.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include + +namespace iue::po { + +inline std::vector init(int argc, char* argv[]) { + return {argv + 1, argv + argc}; +} + +enum struct option { optional, required }; + +template +inline T get(std::vector args, std::string option, T default_value, enum option op = option::optional) { + auto find_option = [&option](const std::string& arg) -> bool { + return arg.starts_with("-" + option) || arg.starts_with("--" + option); + }; + const auto count = std::ranges::count_if(args, find_option); + + if (count == 0) { + if (op == option::required) { + throw std::runtime_error("required option not provided: -/--" + option); + } + + return default_value; + } + + if (count > 1) { + throw std::runtime_error("option not unique: -/--" + option + " (count = " + std::to_string(count) + ")"); + } + + auto it = std::ranges::find_if(args, find_option); + auto value = std::next(it); + + if (value == args.end()) { + throw std::runtime_error("option not followed by value: -/--" + option); + } + + if (value->starts_with('-')) { + throw std::runtime_error("option -/--" + option + " not followed by value but another option -/--" + *value); + } + + std::istringstream ss(*value); + ss.exceptions(std::ios::failbit); + + T num; + + try { + ss >> num; + } catch (std::exception& e) { + throw std::runtime_error("value for option -/--" + option + " could not be converted"); + } + + return num; +} + +} // namespace iue::po diff --git a/lab3/modules/iue-po/po.test.cpp b/lab3/modules/iue-po/po.test.cpp new file mode 100644 index 0000000..ff76af5 --- /dev/null +++ b/lab3/modules/iue-po/po.test.cpp @@ -0,0 +1,23 @@ + +#include "iue-po/po.hpp" + +#include // assert +#include // std::cout|endl + +int main(int argc, char* argv[]) { + + auto args = iue::po::init(argc, argv); + auto value1 = iue::po::get(args, "option1", 1, iue::po::option::required); + auto value2 = iue::po::get(args, "option2", std::string("default2"), iue::po::option::required); + auto value3 = iue::po::get(args, "option3", std::string("default3")); + + std::cout << value1 << std::endl; + std::cout << value2 << std::endl; + std::cout << value3 << std::endl; + + assert(value1 == 100); + assert(value2 == "o2"); + assert(value3 == "default3"); + + return 0; +} diff --git a/lab3/modules/iue-rnd/CMakeLists.txt b/lab3/modules/iue-rnd/CMakeLists.txt new file mode 100644 index 0000000..b8240cc --- /dev/null +++ b/lab3/modules/iue-rnd/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.28) + +# define CXX interface library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-rnd_rnd INTERFACE) + + # define sources of target + target_sources(iue-rnd_rnd + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES random.hpp) + + # define installation step + install(TARGETS iue-rnd_rnd EXPORT iue-rnd-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-rnd_rnd.test random.test.cpp) + target_link_libraries(iue-rnd_rnd.test iue-rnd_rnd) + add_test(NAME iue-rnd_rnd.test COMMAND iue-rnd_rnd.test) +endif() + +# export targets for consuming cmake projects + + # generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets + install(EXPORT iue-rnd-export DESTINATION "iue-rnd" ) + + # allow dependend projects consuming the "build-tree" to use all targets + export(EXPORT iue-rnd-export) diff --git a/lab3/modules/iue-rnd/random.hpp b/lab3/modules/iue-rnd/random.hpp new file mode 100644 index 0000000..209e87b --- /dev/null +++ b/lab3/modules/iue-rnd/random.hpp @@ -0,0 +1,128 @@ +/// @file +/// @brief Generators for uniform distributions of Values, Circles and Triangles + +#pragma once + +#include // std::array +#include // std::sqrt, std::sin, std::cos +#include // std::numbers::pi +#include // std::mt19937, std::uniform_real_distribution +#include // std::tuple + +namespace iue::rnd { + +using Vec2d = std::array; +using Circle = std::tuple; +using Triangle = std::array; + +///@brief Uniformly distributed speudorandom floating point values +struct UniformValue { + + std::mt19937 algo; ///< Random number generator + std::uniform_real_distribution dist; ///< Interval and distribution type + + /// @brief Constructs an instance producing random values uniformly distributed over an interval + /// @param min Lower bound of the interval + /// @param max Upper bound of the interval + /// @param seed Seed used to initialize the generator algorithm + UniformValue(const double min, const double max, const std::size_t seed = 1) : algo(seed), dist(min, max) {} + + /// @brief Obtains the next random value using current state of the generator + /// @return Random value + double operator()() { return dist(algo); } +}; + +///@brief Uniformly distributed circles +struct UniformCircle { + + std::mt19937 algo; ///< Random number generator + std::uniform_real_distribution x; ///< Interval and distribution type for the x-coordinate + std::uniform_real_distribution y; ///< Interval and distribution type for the y-coordinate + std::uniform_real_distribution r; ///< Interval and distribution type for the r-coordinate + + /// @brief Constructs an instance producing random circles uniformly distributed (center and radius) + /// @param c_min Lower bound of the coordinate of the center + /// @param c_max Upper bound of the coordinate of the center + /// @param r_min Lower bound of the radius + /// @param r_max Upper ound of the radius + /// @param seed Seed used to initialize the generator algorithm + UniformCircle(const Vec2d& c_min, const Vec2d& c_max, double r_min, double r_max, const std::size_t seed = 1) + : algo(seed), x(c_min[0], c_max[0]), y(c_min[1], c_max[1]), r(r_min, r_max) {} + + /// @brief Obtains the next random circle using current state of the generator + /// @return Random circle + std::tuple, double> operator()() { + // random center + Vec2d center = {x(algo), y(algo)}; + // random raidus + double radius = r(algo); + // return final configuration + return {center, radius}; + } +}; + +///@brief Uniformly distributed triangles +struct UniformTriangle { + + std::mt19937 algo; ///< Random number generators + std::uniform_real_distribution c_x; ///< distribution for the x-coordinate of the circumcircle + std::uniform_real_distribution c_y; ///< distribution for the y-coordinate of the circumcircle + std::uniform_real_distribution c_r; ///< distribution for the radius of the circumcircle + std::uniform_real_distribution angle120; ///< distribution for the angles of the corner points + std::uniform_real_distribution angle360; ///< distribution for the random rotation of the triangle + + /// @brief Constructs an instance producing random triangles with + /// - uniformly distributed circumcircles, and + /// - uniformly distributed corner points w.r.t. three fixed non-overlapping 120-degree segments on the unit circle + /// @param c_min Lower bound of the coordinate of the circumcirle + /// @param c_max Upper bound of the coordinate of the circumcirle + /// @param r_min Lower bound of the radius of the circle + /// @param r_max Upper ound of the radius of the circle + /// @param seed Seed used to initialize the generator algorithm + UniformTriangle(const Vec2d& c_min, const Vec2d& c_max, double r_min, double r_max, const std::size_t seed = 1) + : algo(seed), c_x(c_min[0], c_max[0]), c_y(c_min[1], c_max[1]), c_r(r_min, r_max), + angle120(0, std::numbers::pi * 2 / 3), angle360(0, std::numbers::pi * 2) {} + + /// @brief Obtains the next random triangle using current state of the generator + /// @return Random circle + Triangle operator()() { + + // three points on the unit circle constraint to three 120deg-segments + // these will be the points of the triangle + double seg = angle120.max(); + double alpha = seg * 0 + angle120(algo); + double beta = seg * 1 + angle120(algo); + double gamma = seg * 2 + angle120(algo); + + Vec2d a = {std::sin(alpha), std::cos(alpha)}; + Vec2d b = {std::sin(beta), std::cos(beta)}; + Vec2d c = {std::sin(gamma), std::cos(gamma)}; + + Triangle abc = {a, b, c}; + + // random circumcircle center + Vec2d center = {c_x(algo), c_y(algo)}; + // random circumcircle radius + double r = c_r(algo); + // random rotation + double rot = angle360(algo); + + auto rotate = [rot](const Vec2d& p) -> const Vec2d { + return {std::cos(rot) * p[0] + (-std::sin(rot)) * p[1], std::sin(rot) * p[0] + std::cos(rot) * p[1]}; + }; + auto shift = [center](const Vec2d& p) -> const Vec2d { return {p[0] + center[0], p[1] + center[1]}; }; + auto scale = [r](const Vec2d& p) -> const Vec2d { return {p[0] * r, p[1] * r}; }; + + // rotate/scale/shift to final configuration + for (auto& coord : abc) { + coord = rotate(coord); + coord = scale(coord); + coord = shift(coord); + } + + // return final configuration + return {abc}; + } +}; + +} // namespace iue::rnd diff --git a/lab3/modules/iue-rnd/random.test.cpp b/lab3/modules/iue-rnd/random.test.cpp new file mode 100644 index 0000000..232be6c --- /dev/null +++ b/lab3/modules/iue-rnd/random.test.cpp @@ -0,0 +1,93 @@ +/// @file +/// @brief Test for iue::rnd::UniformValue, iue::rnd::UniformTriangle, and iue::rnd::UniformCircle + +#include // assert +#include // std::accumulate +#include // std::vector + +#include "iue-rnd/random.hpp" // iue::rnd::UniformValue, iue::rnd::UniformTriangle, iue::rnd::UniformCircle + +#include // iue::num::isclose + +int main() { + + { // random value + std::vector items; + + auto gen = iue::rnd::UniformValue(1.0, 2.0); + size_t N = 1e5; + for (int n = 0; n != N; ++n) + items.push_back({gen()}); + + // mean + auto get_value = [](double sum, const double& value) { return sum + value; }; + auto mean = std::accumulate(items.begin(), items.end(), 0.0, get_value) / items.size(); + assert(iue::num::isclose(mean, 1.5, 1e3 / N)); + } + + { // random circle + std::vector items; + + // generate random circles + iue::rnd::Vec2d min = {1, 1}; + iue::rnd::Vec2d max = {2, 2}; + auto gen = iue::rnd::UniformCircle(min, max, 1, 2); + size_t N = 1e5; + for (int n = 0; n != N; ++n) + items.push_back({gen()}); + + // r_mean + auto get_r = [](double sum, const iue::rnd::Circle& circ) { + const auto& [c, r] = circ; + return sum + r; + }; + auto r_mean = std::accumulate(items.begin(), items.end(), 0.0, get_r) / items.size(); + assert(iue::num::isclose(r_mean, 1.5, 1e3 / N)); + + // cx_mean + auto get_cx = [](double sum, const iue::rnd::Circle& circ) { + const auto& [c, r] = circ; + return sum + c[0]; + }; + auto cx_mean = std::accumulate(items.begin(), items.end(), 0.0, get_cx) / items.size(); + assert(iue::num::isclose(cx_mean, 1.5, 1e3 / N)); + + // cy_mean + auto get_cy = [](double sum, const iue::rnd::Circle& circ) { + const auto& [c, r] = circ; + return sum + c[1]; + }; + auto cy_mean = std::accumulate(items.begin(), items.end(), 0.0, get_cy) / items.size(); + assert(iue::num::isclose(cy_mean, 1.5, 1e3 / N)); + } + + { // random triangle + std::vector items; + + // generate random circles + iue::rnd::Vec2d min = {1, 1}; + iue::rnd::Vec2d max = {2, 2}; + auto gen = iue::rnd::UniformTriangle(min, max, 1, 2); + size_t N = 1e5; + for (int n = 0; n != N; ++n) + items.push_back({gen()}); + + // abcx_mean + auto get_x = [](double sum, const iue::rnd::Triangle& tri) { + const auto& [a, b, c] = tri; + return sum + a[0] + b[0] + c[0]; + }; + auto abcx_mean = std::accumulate(items.begin(), items.end(), 0.0, get_x) / items.size() / 3.0; + assert(iue::num::isclose(abcx_mean, 1.5, 1e3 / N)); + + // abcx_mean + auto get_y = [](double sum, const iue::rnd::Triangle& tri) { + const auto& [a, b, c] = tri; + return sum + a[1] + b[1] + c[1]; + }; + auto abcy_mean = std::accumulate(items.begin(), items.end(), 0.0, get_y) / items.size() / 3.0; + assert(iue::num::isclose(abcy_mean, 1.5, 1e3 / N)); + } + + return 0; +} diff --git a/lab3/modules/iue-svg/CMakeLists.txt b/lab3/modules/iue-svg/CMakeLists.txt new file mode 100644 index 0000000..16a1ec6 --- /dev/null +++ b/lab3/modules/iue-svg/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.28) + +# define CXX interface library + + # define target & alias (for other projects consuming the "source-tree") + add_library(iue-svg_svg INTERFACE) + + # define sources of target + target_sources(iue-svg_svg + INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES tags.hpp tree.hpp render.hpp) + + # define installation step + install(TARGETS iue-svg_svg EXPORT iue-svg-export + FILE_SET public_headers DESTINATION "./") + + # define test + +if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL) + add_executable(iue-svg_tree.test tree.test.cpp) + target_link_libraries(iue-svg_tree.test iue-svg_svg) + add_test(NAME iue-svg_tree.test COMMAND iue-svg_tree.test) + + add_executable(iue-svg_tags.test tags.test.cpp) + target_link_libraries(iue-svg_tags.test iue-svg_svg) + add_test(NAME iue-svg_tags.test COMMAND iue-svg_tags.test) + + add_executable(iue-svg_render.test render.test.cpp) + target_link_libraries(iue-svg_render.test iue-svg_svg) + add_test(NAME iue-svg_render.test COMMAND iue-svg_render.test) + +endif() + +# export targets for consuming cmake projects + + # generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets + install(EXPORT iue-svg-export DESTINATION "iue-svg" ) + + # allow dependend projects consuming the "build-tree" to use all targets + export(EXPORT iue-svg-export) diff --git a/lab3/modules/iue-svg/render.hpp b/lab3/modules/iue-svg/render.hpp new file mode 100644 index 0000000..6923a0d --- /dev/null +++ b/lab3/modules/iue-svg/render.hpp @@ -0,0 +1,157 @@ +/// @file +/// @brief Educational implementation of a SVG-Writer + +#pragma once + +#include "iue-svg/tags.hpp" // iue::svg:::XMLTag, iue::svg::svg +#include "iue-svg/tree.hpp" // iue::svg::Tree + +#include // assert +#include // std::filesystem::path +#include // std::ofstream +#include // std::stringstream +#include // std::string +#include // std::vector + +namespace iue::svg { + +/// @brief Generate a SVG file from sequences of compatible primitives +/// @return Enclosing bounding box (original unscaled coordinates) +/// Imporant notes: +/// 1. The outline of the svg will be the bounding box enclosing all occuring geometric primitives (based on coordinate +/// based) +/// - the detail of the extend of an object (textlength, fontsize, linewidth, ...) is **not** considered +/// 2. The coordinates are **scaled/rotated/translated before serializing** +/// - this is not so nice, but a scaling is required to improve the viewing experience for small coordinate spans +/// 3. All primitives are rendered with black lines and no fill +/// - the linewidth is choosen relative the the dimension of the enclosing bounding box +/// - the font-size for text is relative to the linewith +inline iue::svg::BBox render(std::filesystem::path filepath, const std::vector& bboxes, + const std::vector& circles, const std::vector& triangles) { + + using XMLTree = Tree; + + assert(bboxes.size() != 0 || circles.size() != 0 || triangles.size() != 0); + + // calculate maximum extends + Vec2d bbmin = {+std::numeric_limits::max(), +std::numeric_limits::max()}; + Vec2d bbmax = {-std::numeric_limits::max(), -std::numeric_limits::max()}; + + // update minmax from single coordinate + auto minmax = [&bbmin, &bbmax](const Vec2d& p) { + bbmin[0] = bbmin[0] < p[0] ? bbmin[0] : p[0]; + bbmin[1] = bbmin[1] < p[1] ? bbmin[1] : p[1]; + bbmax[0] = bbmax[0] > p[0] ? bbmax[0] : p[0]; + bbmax[1] = bbmax[1] > p[1] ? bbmax[1] : p[1]; + }; + + for (const auto& circle : circles) { + const auto& [c, r] = circle; + minmax({c[0] - r, c[1] - r}); + minmax({c[0] + r, c[1] + r}); + } + + for (const auto& bbox : bboxes) { + const auto& [min, max] = bbox; + minmax(min); + minmax(max); + } + + for (const auto& triangle : triangles) { + for (const auto& p : triangle) + minmax(p); + } + + // scaling + double resolution; + { + auto const& [xmin, ymin] = bbmin; + auto const& [xmax, ymax] = bbmax; + double width = std::abs(xmax - xmin); + double height = std::abs(ymax - ymin); + resolution = std::max(width, height); + } + double unit = resolution / 800; // stroke referens is "thin" for a reference resolution + double scale = 1 / unit; + + // scaling to have a unit stroke of 1 + auto sc = [scale](const Vec2d& p) -> Vec2d { return {p[0] * scale, p[1] * scale}; }; + auto scd = [scale](const double& val) -> double { return val * scale; }; + + // transform to have bottom left origin (svg default is top left origin) + auto tf = [xmin = bbmin[0], ymax = bbmax[1]](const Vec2d& p) -> Vec2d { return {(p[0] - xmin), (-p[1] + ymax)}; }; + + // create tree with svg + XMLTree dom{svg(sc(bbmin), sc(bbmax))}; + dom.org.nodes.push_back({svg::group()}); + auto& g = dom.org.nodes.back(); + + { // plot all triangles + for (const auto& triangle : triangles) { + const auto& [a, b, c] = triangle; + g.nodes.push_back({svg::polygon({sc(tf(a)), sc(tf(b)), sc(tf(c))})}); + } + } + + { // plot all BBoxes + for (const auto& bbox : bboxes) { + const auto& [min, max] = bbox; + const auto& [xmin, ymin] = min; + const auto& [xmax, ymax] = max; + std::vector coords; + coords.push_back(sc(tf({xmin, ymin}))); + coords.push_back(sc(tf({xmax, ymin}))); + coords.push_back(sc(tf({xmax, ymax}))); + coords.push_back(sc(tf({xmin, ymax}))); + g.nodes.push_back({svg::polygon(coords)}); + } + } + + { // plot all circles + for (const auto& circle : circles) { + const auto& [c, r] = circle; + g.nodes.push_back({svg::circle({sc(tf(c)), scd(r)})}); + } + } + + { // plot bounding box of viewport + const auto& [xmin, ymin] = bbmin; + const auto& [xmax, ymax] = bbmax; + std::vector limits; + limits.push_back(sc(tf({xmin, ymin}))); + limits.push_back(sc(tf({xmax, ymin}))); + limits.push_back(sc(tf({xmax, ymax}))); + limits.push_back(sc(tf({xmin, ymax}))); + g.nodes.push_back({svg::polygon(limits)}); + } + + // serialize tree to string + std::stringstream stream; + { + + auto down = [&stream](const XMLTag& value, std::size_t level) -> void { + stream << std::string(2 * level, ' ') << value.open() << std::endl; + auto text = value.text(); + if (!text.empty()) + stream << std::string(2 * level, ' ') << value.text() << std::endl; + }; + + auto up = [&stream](const XMLTag& value, std::size_t level) -> void { + auto close = value.close(); + if (!close.empty()) + stream << std::string(2 * level, ' ') << value.close() << std::endl; + }; + + XMLTree::preorder(dom.org, down, up); + } + + // save as file + std::ofstream fs; + fs.exceptions(std::ifstream::badbit); + fs.open(filepath); + fs << stream.str(); + + return {bbmin, bbmax}; +} + +} // namespace iue::svg diff --git a/lab3/modules/iue-svg/render.test.cpp b/lab3/modules/iue-svg/render.test.cpp new file mode 100644 index 0000000..ef6d6e6 --- /dev/null +++ b/lab3/modules/iue-svg/render.test.cpp @@ -0,0 +1,37 @@ +/// @file +/// @brief Test for 'iue::svg::render' + +#include "iue-svg/render.hpp" // iue::svg::render +#include "iue-num/numerics.hpp" // iue::num::isclose +#include "iue-svg/tags.hpp" // iue::svg::Circle, iue::svg::Triangle, iue::svg::BBox + +int main() { + + std::vector circles; + circles.push_back(iue::svg::Circle{{1, 1}, 1}); + circles.push_back(iue::svg::Circle{{2, 2}, 2}); + circles.push_back(iue::svg::Circle{{3, 3}, 3}); + circles.push_back(iue::svg::Circle{{10, 10}, 10}); + + std::vector triangles; + triangles.push_back(iue::svg::Triangle{{{50, 50}, {49, 50}, {50, 49}}}); + triangles.push_back(iue::svg::Triangle{{{50, 50}, {48, 50}, {50, 48}}}); + triangles.push_back(iue::svg::Triangle{{{50, 50}, {47, 50}, {50, 47}}}); + triangles.push_back(iue::svg::Triangle{{{50, 50}, {40, 50}, {50, 40}}}); + + std::vector bboxes; + bboxes.push_back(iue::svg::BBox{{0, 49}, {1, 50}}); + bboxes.push_back(iue::svg::BBox{{0, 48}, {2, 50}}); + bboxes.push_back(iue::svg::BBox{{0, 47}, {4, 50}}); + bboxes.push_back(iue::svg::BBox{{0, 40}, {10, 50}}); + + auto [bbmin, bbmax] = iue::svg::render("render.test.svg", bboxes, circles, triangles); + + assert(iue::num::isclose(bbmin[0], 0)); + assert(iue::num::isclose(bbmin[1], 0)); + + assert(iue::num::isclose(bbmax[0], 50)); + assert(iue::num::isclose(bbmax[1], 50)); + + return 0; +} diff --git a/lab3/modules/iue-svg/tags.hpp b/lab3/modules/iue-svg/tags.hpp new file mode 100644 index 0000000..cc67267 --- /dev/null +++ b/lab3/modules/iue-svg/tags.hpp @@ -0,0 +1,151 @@ +/// @file +/// @brief Implementation of SVG-Tags for the SVG-writer in render.hpp + +#pragma once + +#include // std::array +#include // std::stringstream +#include // std::string +#include // std::unordered_map +#include // std::vector + +namespace iue::svg { + +/// @brief convenience aliases for short notations +using Vec2d = std::array; +using Circle = std::tuple; +using Polygon = std::vector; +using Triangle = std::array; +using BBox = std::tuple; +using Text = std::tuple; + +/// @brief Map for storing an open set of attribute/value pairs +using Attributes = std::unordered_map; + +/// @brief Requirements towards an XMLTag +template +concept Tag = requires(const T& t) { + { t.open() } -> std::same_as; + { t.text() } -> std::same_as; + { t.close() } -> std::same_as; +}; + +/// @brief Implementation of an SVG XML-tag +struct XMLTag { + std::string name; + Attributes attributes; + std::string content; + bool self_closing; + std::string text() const { return this->content; } + std::string open() const { + std::stringstream res; + res << "<" << name; + for (auto const& [key, val] : attributes) + res << " " << key << "='" << val << "'"; + if (self_closing) + res << "/>"; + else + res << ">"; + return res.str(); + } + std::string close() const { + std::stringstream res; + if (!self_closing) + res << ""; + return res.str(); + } +}; + +/// @brief Check if Tag requirements are fullfilled +static_assert(Tag); + +/// @brief Root SVG Element +/// +inline XMLTag svg(const Vec2d& bbmin, const Vec2d& bbmax) { + Attributes attributes; + // add data attributes + auto const& [xmin, ymin] = bbmin; + auto const& [xmax, ymax] = bbmax; + double width = std::abs(xmax - xmin); + double height = std::abs(ymax - ymin); + attributes.insert({"width", std::to_string(width)}); + attributes.insert({"height", std::to_string(height)}); + std::stringstream viewbox; + for (const auto& val : {xmin, ymin, width, height}) + viewbox << val << " "; + attributes.insert({"viewbox", viewbox.str()}); + // add default attributes + attributes.insert({"xmlns", "http://www.w3.org/2000/svg"}); + attributes.insert({"version", "1.1"}); + attributes.insert({"preserveAspectRatio", "xMidYMid meet"}); + // final assembly + return {"svg", attributes, std::string{}, false}; +} + +/// @brief Group SVG Element +/// +inline XMLTag group(const Attributes& custom_attributes = {}) { + Attributes attributes; + // add custom attributes + attributes.insert(custom_attributes.begin(), custom_attributes.end()); + // final assembly + return {"g", attributes, std::string{}, false}; +} + +/// @brief Circle SVG Element +/// +inline XMLTag circle(const Circle& circle, const Attributes& custom_attributes = {}) { + Attributes attributes; + // add data attributes + const auto& [center, radius] = circle; + const auto& [cx, cy] = center; + attributes.insert({"cx", std::to_string(cx)}); + attributes.insert({"cy", std::to_string(cy)}); + attributes.insert({"r", std::to_string(radius)}); + // add custom attributes + attributes.insert(custom_attributes.begin(), custom_attributes.end()); + // add default attributes + attributes.insert({"stroke-width", std::to_string(1)}); + attributes.insert({"stroke", "black"}); + attributes.insert({"fill", "none"}); + // final assembly + return {"circle", attributes, std::string{}, true}; +} + +/// @brief Polygon SVG Element +/// +inline XMLTag polygon(const Polygon& polygon, const Attributes& custom_attributes = {}) { + Attributes attributes; + // add data attributes + std::stringstream points; + for (const auto& [x, y] : polygon) + points << x << "," << y << " "; + attributes.insert({"points", points.str()}); + // add custom attributes + attributes.insert(custom_attributes.begin(), custom_attributes.end()); + // add default attributes + attributes.insert({"stroke-width", std::to_string(1)}); + attributes.insert({"stroke", "black"}); + attributes.insert({"fill", "none"}); + // final assembly + return {"polygon", attributes, std::string{}, true}; +} + +/// @brief Text SVG Element +/// content +inline XMLTag text(const Text& text, const Attributes& custom_attributes = {}) { + Attributes attributes; + // add data attributes + const auto& [anchor, content] = text; + const auto& [x, y] = anchor; + attributes.insert({"x", std::to_string(x)}); + attributes.insert({"y", std::to_string(y)}); + // add custom attributes + attributes.insert(custom_attributes.begin(), custom_attributes.end()); + // add default attributes + attributes.insert({"font-size", std::to_string(10)}); + attributes.insert({"text-anchor", "middle"}); + return {"text", attributes, content, false}; +} + +} // namespace iue::svg diff --git a/lab3/modules/iue-svg/tags.test.cpp b/lab3/modules/iue-svg/tags.test.cpp new file mode 100644 index 0000000..2c7a5e7 --- /dev/null +++ b/lab3/modules/iue-svg/tags.test.cpp @@ -0,0 +1,62 @@ +/// @file +/// @brief Test for iue::svg::XMLTag + +#include "iue-svg/tags.hpp" // iue::svg::XMLTag, iue::svg::svg, iue::svg::circle, iue::svg::polygon, iue::svg::, iue::svg::text + +#include // EXIT_SUCCESS +#include // std::cout, std::endl +#include // std::string + +struct Value { + int a = 1; +}; + +int main() { + + { // w/ default attributes + auto svg = iue::svg::svg({0, 0}, {100, 100}); + auto group = iue::svg::group(); + auto text = iue::svg::text({{50, 50}, "text"}); + auto circle = iue::svg::circle({{50, 50}, 0.5}); + auto polygon = iue::svg::polygon({{50, 50}, {52, 51}, {51, 52}, {78, 100}}); + + std::cout << svg.open() << std::endl; + std::cout << group.open() << std::endl; + std::cout << text.open() << std::endl; + std::cout << text.text() << std::endl; + std::cout << text.close() << std::endl; + std::cout << circle.open() << std::endl; + std::cout << circle.close() << std::endl; + std::cout << polygon.open() << std::endl; + std::cout << polygon.close() << std::endl; + std::cout << group.close() << std::endl; + std::cout << svg.close() << std::endl; + } + + { // w/ custom attributes + iue::svg::Attributes attributes; + attributes.insert({"stroke-width", std::to_string(10)}); + attributes.insert({"stroke", "green"}); + attributes.insert({"fill", "blue"}); + attributes.insert({"invalid-attribute", "no check is performed"}); + auto svg = iue::svg::svg({0, 0}, {100, 100}); + auto group = iue::svg::group(attributes); + auto text = iue::svg::text({{50, 50}, "text"}, attributes); + auto circle = iue::svg::circle({{50, 50}, 0.5}, attributes); + auto polygon = iue::svg::polygon({{50, 50}, {52, 51}, {51, 52}, {78, 100}}, attributes); + + std::cout << svg.open() << std::endl; + std::cout << group.open() << std::endl; + std::cout << text.open() << std::endl; + std::cout << text.text() << std::endl; + std::cout << text.close() << std::endl; + std::cout << circle.open() << std::endl; + std::cout << circle.close() << std::endl; + std::cout << polygon.open() << std::endl; + std::cout << polygon.close() << std::endl; + std::cout << group.close() << std::endl; + std::cout << svg.close() << std::endl; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/modules/iue-svg/tree.hpp b/lab3/modules/iue-svg/tree.hpp new file mode 100644 index 0000000..2618310 --- /dev/null +++ b/lab3/modules/iue-svg/tree.hpp @@ -0,0 +1,56 @@ +/// @file +/// @brief Educational implementation of a tree data structure with value semantics +/// @note The implementation relies on the "minimal incomplete type support" incomplete type support for std::list +/// - https://en.cppreference.com/w/cpp/feature_test#cpp_lib_incomplete_container_elements + +#pragma once + +#include // std::size_t +#include // std::function +#include // std::string + +namespace iue::svg { + +template +concept Value = requires(const T& a, T& b) { + T(a); ///< copy constructor (std::copy_constructible) + b = a; ///< copy assignment (std::assignable_from) +}; + +/// @brief Generic tree (tree with a dynamic unbound number of child nodes) +template struct Tree { + + /// @brief Node in the tree + struct Node { + Value value; ///< Stored value + std::list nodes; ///< List of child nodes + }; + + using ValueVisitor = std::function; + using NodeVisitor = std::function; + + Node org; ///< Root node of the tree + + /// @brief Constructs a tree + /// @param Value held by the root node of the tree + /// @note This constructor avoids that the value type needs to be default-constructible + Tree(const Value& value) : org{value} {} + + /// @brief Visit all values in the nodes in preorder using two visitors for down and up path + static void preorder(Node& root, const ValueVisitor& down, const ValueVisitor& up, std::size_t level = 0) { + down(root.value, level); + for (auto& child : root.nodes) + preorder(child, down, up, level + 1); + up(root.value, level); + } + + /// @brief Visit all nodes in preorder using two visitors for down and up path + static void preorder(Node& root, const NodeVisitor& down, const NodeVisitor& up, std::size_t level = 0) { + down(root, level); + for (auto& child : root.nodes) + preorder(child, down, up, level + 1); + up(root, level); + } +}; + +} // namespace iue::svg diff --git a/lab3/modules/iue-svg/tree.test.cpp b/lab3/modules/iue-svg/tree.test.cpp new file mode 100644 index 0000000..b9a7f88 --- /dev/null +++ b/lab3/modules/iue-svg/tree.test.cpp @@ -0,0 +1,60 @@ +/// @file +/// @brief Test for iue::svg::Tree + +#include "iue-svg/tree.hpp" // iue::svg::Tree + +#include // assert +#include // std::size_t +#include // EXIT_SUCCESS +#include // std::cout, std::endl +#include // std::stringstream +#include // std::string + +struct Value { + int a = 1; +}; + +int main() { + + using Tree = iue::svg::Tree; + using Node = Tree::Node; + + Tree tree = {Value{0}}; + + auto& l0 = tree.org; + + l0.nodes.push_back(Node{Value{1}}); + l0.nodes.push_back(Node{Value{1}}); + l0.nodes.push_back(Node{Value{1}}); + + for (auto& l1 : l0.nodes) { + l1.nodes.push_back(Node{Value{2}}); + l1.nodes.push_back(Node{Value{2}}); + l1.nodes.push_back(Node{Value{2}}); + for (auto& l1 : l1.nodes) { + l1.nodes.push_back(Node{Value{3}}); + l1.nodes.push_back(Node{Value{3}}); + } + } + + auto noop = [](const Value& value, std::size_t level) -> void {}; + + { // print tree + std::stringstream stream; + + auto print = [&stream](const Value& value, std::size_t level) -> void { + stream << std::string(2 * level, ' ') << value.a << std::endl; + }; + tree.preorder(l0, print, noop); + std::cout << stream.str() << std::endl; + } + + { // sum values in tree + int sum = 0; + auto count = [&sum](const Value& value, std::size_t level) -> void { sum += value.a; }; + tree.preorder(l0, count, noop); + assert(sum == 1 * 0 + (1 * 3 * 1) + (1 * 3 * 3 * 2) + (1 * 3 * 3 * 2 * 3)); + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/lab3/taskA.Vector.hpp b/lab3/taskA.Vector.hpp new file mode 100644 index 0000000..0afbcfa --- /dev/null +++ b/lab3/taskA.Vector.hpp @@ -0,0 +1,47 @@ +/// @file +/// @brief task A: Class Vector + +#pragma once + +#include // std::function +#include // std::cout, std::endl +#include // std::vector + +/// @todo implement a class 'Vector' according to the following specification: +/// - use a *private* member variable of type std::vector +/// - provide 3 constructors: +/// 1. passing a std::vector +/// - the values will be copied over to the private member +/// 2. passing a count 'N' and an interval specified by a 'start', and 'end' value +/// - fill the private member with 'N' equidistant samples from the interval +/// 3. passing a count 'N' and a generator function in form of a callable std::function +/// - fill the private member with 'N' samples obtained from the generator/callable +/// - member function print(): +/// - prints all elements in the private member to the console + +class Vector { + std::vector data_; +public: + Vector(std::vector data) : data_(data) {} + Vector(double start, double end, unsigned int N) { + double step = (end - start) / (N - 1); + for (unsigned int i = 0; i < N; ++i) { + data_.push_back(start + i * step); + } + } + Vector(unsigned int N, std::function generator) { + for (unsigned int i = 0; i < N; ++i) { + data_.push_back(generator()); + } + } + void print() { + std::cout << "["; + for (unsigned int i = 0; i < data_.size(); ++i) { + std::cout << data_[i]; + if (i < data_.size() - 1) { + std::cout << ", "; + } + } + std::cout << "]" << std::endl; + } +}; \ No newline at end of file diff --git a/lab3/taskA.VectorT.hpp b/lab3/taskA.VectorT.hpp new file mode 100644 index 0000000..6892b74 --- /dev/null +++ b/lab3/taskA.VectorT.hpp @@ -0,0 +1,37 @@ +/// @file +/// @brief task C: transform class Vector from task A to a class template + +#pragma once +#include +#include +#include + +/// @todo copy Vector from taskA.Vector.hpp and adapt according to task C +template +class Vector { + std::vector data_; + +public: + Vector(std::vector data) : data_(data) {} + Vector(T start, T end, unsigned int N) { + T step = (end - start) / (N - 1); + for (unsigned int i = 0; i < N; ++i) { + data_.push_back(start + i * step); + } + } + Vector(unsigned int N, std::function generator) { + for (unsigned int i = 0; i < N; ++i) { + data_.push_back(generator()); + } + } + void print() { + std::cout << "["; + for (unsigned int i = 0; i < data_.size(); ++i) { + std::cout << data_[i]; + if (i < data_.size() - 1) { + std::cout << ", "; + } + } + std::cout << "]" << std::endl; + } +}; \ No newline at end of file diff --git a/lab3/taskA.cpp b/lab3/taskA.cpp new file mode 100644 index 0000000..83c8250 --- /dev/null +++ b/lab3/taskA.cpp @@ -0,0 +1,34 @@ +/// @file +/// @brief task A +/// g++ -std=c++20 taskA.cpp -Imodules -o taskA && ./taskA + +#include +#include +#include + +#include "iue-rnd/random.hpp" // iue::rnd::UniformValue + +#include "taskA.Vector.hpp" // todo: implement Vector + +int main() { + + // first constructor + std::vector data = {2, 4, 6, 8}; + // Vector v1(data); // @todo: uncomment + + // second constructor + double start = 0; + double end = 5; + unsigned int N = 6; + // Vector v2(start, end, N); // @todo: uncomment + + // third constructor + auto callable = iue::rnd::UniformValue(0.0, 1.0); + // Vector v3(N, callable); // @todo: uncomment + + // v1.print(); // @todo: uncomment [2, 4, 6, 8] + // v2.print(); // @todo: uncomment [0, 1, 2, 3, 4, 5] + // v3.print(); // @todo: uncomment [6 random values between 0..1] + + return 0; +} \ No newline at end of file diff --git a/lab3/taskB.Car.hpp b/lab3/taskB.Car.hpp new file mode 100644 index 0000000..ec02fab --- /dev/null +++ b/lab3/taskB.Car.hpp @@ -0,0 +1,26 @@ +/// @file +/// @brief task B: Car class + +#pragma once + +#include "taskB.Device.hpp" // Device + +/// @todo Implement a class `Car` that publicly derives from 'Device' + +class Car : public Device { +private: + std::string name_; + std::string model_; + double price_; + double power_; + +public: + Car(std::string name, std::string model, double price, double power) + : name_(name), model_(model), price_(price), power_(power) {} + + void print_info() const final override { + std::cout << name_ << ": " << model_ << std::endl; + std::cout << " power: " << power_ << " Kilowatts" << std::endl; + std::cout << " price: " << price_ << " EUR" << std::endl; + } +}; diff --git a/lab3/taskB.Device.hpp b/lab3/taskB.Device.hpp new file mode 100644 index 0000000..41026dc --- /dev/null +++ b/lab3/taskB.Device.hpp @@ -0,0 +1,13 @@ +/// @file +/// @brief task B: Abstract interface class + +#pragma once + +#include +#include + +/// @brief Abstract interface class to a generic "device" +struct Device { + virtual void print_info() const = 0; + virtual ~Device() {} +}; diff --git a/lab3/taskB.Train.hpp b/lab3/taskB.Train.hpp new file mode 100644 index 0000000..baec01b --- /dev/null +++ b/lab3/taskB.Train.hpp @@ -0,0 +1,25 @@ +/// @file +/// @brief task B: Train class + +#pragma once + +#include "taskB.Device.hpp" + +/// @todo Example class `Train` that publicly derives from 'Device' +struct Train : Device { +private: + std::string name_; + std::string description_; + size_t capacity_; + double length_; + +public: + Train(std::string name, std::string description, size_t capacity, double length) + : name_(name), description_(description), capacity_(capacity), length_(length) {} + + void print_info() const final override { + std::cout << name_ << ": " << description_ << std::endl; + std::cout << " length: " << length_ << " Meters" << std::endl; + std::cout << " capacity: " << capacity_ << " Passangers" << std::endl; + } +}; diff --git a/lab3/taskB.WashingMachine.hpp b/lab3/taskB.WashingMachine.hpp new file mode 100644 index 0000000..153c882 --- /dev/null +++ b/lab3/taskB.WashingMachine.hpp @@ -0,0 +1,25 @@ +/// @file +/// @brief task B: WashingMachine class + +#pragma once + +#include "taskB.Device.hpp" + +/// @todo Example class `WashingMachine` that publicly derives from 'Device' +struct WashingMachine : Device { +private: + std::string name_; + std::string description_; + double price_; + double power_; + +public: + WashingMachine(std::string name, std::string description, double price, double power) + : name_(name), description_(description), price_(price), power_(power) {} + + void print_info() const final override { + std::cout << name_ << ": " << description_ << std::endl; + std::cout << " power: " << power_ << " Kilowatts" << std::endl; + std::cout << " price: " << price_ << " EUR" << std::endl; + } +}; diff --git a/lab3/taskB.cpp b/lab3/taskB.cpp new file mode 100644 index 0000000..310984d --- /dev/null +++ b/lab3/taskB.cpp @@ -0,0 +1,35 @@ +/// @file +/// @brief task B +/// g++ -g -fsanitize=address -std=c++20 taskB.cpp -o taskB && ./taskB + +#include + +#include "taskB.Device.hpp" // Device + +#include "taskB.Train.hpp" // Train : Device +#include "taskB.WashingMachine.hpp" // WashingMachine : Device + +#include "taskB.Car.hpp" // todo: implement Car : Device + +int main() { + + std::vector devices; ///< contains pointers to dynamically allocated objects + + devices.push_back(new WashingMachine("Miele W1", "will last forever (if you maintain it well)", 1500, 1200)); + devices.push_back(new WashingMachine("Constructa XZY", "will also last forever (if you maintain it well)", 500, 800)); + devices.push_back(new Train("ICE 4", "solid and fast train", 820, 350)); + devices.push_back(new Train("Railjet I", "one of the world fastest trains with a discrete locomotive", 400, 200)); + + /// @todo: create dynamically allocated instances of your Car/Train classes and push them to 'devices' + + + /// loop over the vector and making use of the interface function 'print_info' + for (const auto& d : devices) { + d->print_info(); + } + + /// @todo: delete all dynamically allocated objects stored in 'devices' using 'delete' (use a loop) + + + return 0; +} diff --git a/lab3/taskC.cpp b/lab3/taskC.cpp new file mode 100644 index 0000000..62c10f0 --- /dev/null +++ b/lab3/taskC.cpp @@ -0,0 +1,27 @@ +/// @file +/// @brief task C +/// g++ -g -std=c++20 taskC.cpp -o taskC && ./taskC + +#include "taskC.one.hpp" // regular functions: one::add, one::sum +#include "taskC.two.hpp" // todo: implement function templates two::add, two::sum + +int main() { + + using namespace one; // todo: switch to namespace 'two' after implementing the function templates, details see taskC.two.hpp + + int x = 5; + int y = 6; + double u = 1.2; + double v = 3.3; + + int z = add(x, y); + double w = add(u, v); + + std::vector xV = {1, 2, 3}; + std::vector yV = {5.1, 4.9, 10.0}; + + int sumI = sum(xV); + double sumD = sum(yV); + + return 0; +} \ No newline at end of file diff --git a/lab3/taskC.one.hpp b/lab3/taskC.one.hpp new file mode 100644 index 0000000..1e641e4 --- /dev/null +++ b/lab3/taskC.one.hpp @@ -0,0 +1,31 @@ +/// @file +/// @brief task C: regular functions with overloads (no templates) + +#pragma once + +#include + +namespace one { + +inline int add(const int& a, const int& b) { + return a + b; +} +inline double add(const double& a, const double& b) { + return a + b; +} + +inline int sum(const std::vector& vec) { + int res = 0; + for (int x : vec) { + res += x; + } + return res; +} +inline double sum(const std::vector& vec) { + double res = 0; + for (double x : vec) { + res += x; + } + return res; +} +} // namespace one diff --git a/lab3/taskC.two.hpp b/lab3/taskC.two.hpp new file mode 100644 index 0000000..2671883 --- /dev/null +++ b/lab3/taskC.two.hpp @@ -0,0 +1,27 @@ +/// @file +/// @brief task C: function templates + +#pragma once + +#include + +namespace two { + +/// @todo: implement ONE function template covering the function overloads 'one::add' +/// @todo: implement ONE function template covering the function overloads 'one::sum' + +template +T add(const T& a, const T& b) { + return a + b; +} + +template +T sum(const std::vector& vec) { + T res = 0; + for (T x : vec) { + res += x; + } + return res; + + +} // namespace two \ No newline at end of file diff --git a/lab5/.clang-format b/lab5/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/lab5/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/lab5/.clangd b/lab5/.clangd new file mode 100644 index 0000000..0e2a4e2 --- /dev/null +++ b/lab5/.clangd @@ -0,0 +1,23 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: No + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\\.c, .*\\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\\.cpp, .*\\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/lab5/.gitattributes b/lab5/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab5/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab5/.gitignore b/lab5/.gitignore new file mode 100644 index 0000000..9146a80 --- /dev/null +++ b/lab5/.gitignore @@ -0,0 +1,364 @@ +# custom + +taskA +taskB +taskC +taskAref +taskBref +taskCref +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab5/.gitmodules b/lab5/.gitmodules new file mode 100644 index 0000000..66c7f1a --- /dev/null +++ b/lab5/.gitmodules @@ -0,0 +1,4 @@ +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/lab5/CMakeLists.txt b/lab5/CMakeLists.txt new file mode 100644 index 0000000..852724e --- /dev/null +++ b/lab5/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(lab5 LANGUAGES C CXX + DESCRIPTION "lab5" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/lab5") + +# setting required language standards + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED True) + set(CMAKE_C_EXTENSIONS OFF) + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# find math library and link to all targets + + find_library(MATH_LIBRARY m) + link_libraries(${MATH_LIBRARY}) + +# global includes + + include_directories(modules) + +# include own targets + + add_executable(taskA taskA.c) + add_executable(taskB taskB.c) + add_executable(taskC taskC.c) + diff --git a/lab5/README.md b/lab5/README.md new file mode 100644 index 0000000..4c5745d --- /dev/null +++ b/lab5/README.md @@ -0,0 +1,25 @@ +# Labor V: Abgabe (30min) + +**Dieser Teil wird über [TUWEL, Labor V: Abgabe (30min)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse13)** abgewickelt. + +# Labor V: Praxisteil (120min) + +- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**. +- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint. +- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben. +- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben. +- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren. + +--- + +Im heutigen Labor sollen Sie die folgenden drei Aufgabengebiete bearbeiten. + +### A. Kommandozeilenoptionen + +### B. Verwendung einer gegebenen Bibliothek zum Lesen/Schreiben von .csv-Dateien (C-Version) + +### C. Vergleich von numerischen Daten + +--- + +Details zu den Aufgaben finden Sie in [`main.ipynb`](main.ipynb). diff --git a/lab5/compile_flags.txt b/lab5/compile_flags.txt new file mode 100644 index 0000000..bde723f --- /dev/null +++ b/lab5/compile_flags.txt @@ -0,0 +1 @@ +-Imodules \ No newline at end of file diff --git a/lab5/main.ipynb b/lab5/main.ipynb new file mode 100644 index 0000000..481ede3 --- /dev/null +++ b/lab5/main.ipynb @@ -0,0 +1,111 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A. Kommandozeilenoptionen\n", + "\n", + "**Aufgabe (Details siehe [taskA.c](taskA.c)):**\n", + "- Implementieren Sie mithilfe der in [modules/iue-po/cpo.h](modules/iue-po/cpo.h) bereitgestellten Funktionalität ein Programm mit einer Kommandozeilen-Schnittstelle:\n", + " - Es gibt vier Argumente, die über die Befehlszeile übergeben werden können: \n", + " - erforderlich: ein ganzzahliger Wert (`opt1`)\n", + " - erforderlich: eine Fließkommazahl (`opt2`)\n", + " - optional: eine Zeichen (Typ `char`) (`opt3`), Standardwert `A`\n", + " - optional: Zeichenkette (`opt4`), Standardwert `\"empty\"`\n", + " - Gültige Aufrufe Ihres Programms können dann z.B. so aussehen:\n", + " ```shell\n", + " ./taskA -opt1 10 -opt2 1.5 \n", + " ./taskA -opt1 10 -opt2 1.5 -opt3 X -opt4 lab5\n", + " ./taskA -opt2 1.5 -opt3 Y -opt4 lab5 -opt1 10 \n", + " ```\n", + " - Geben Sie die übergebenen Werte zur Überprüfung in der Konsole aus.\n", + " - Falls ein notwendiges Argumente nicht übergeben wurde (oder nicht erfolgreich zum Zieltyp umgewandelt werden konnte), soll Ihr Programm mit einem Fehler abbrechen.\n", + "\n", + "**Hinweise:**\n", + "- In der Datei [modules/iue-po/cpo.test.c](modules/iue-po/cpo.test.c) befindet sich ein Test, der die erforderlichen Funktionen einsetzt:\n", + " - Sie können dieses Programm als Vorlage benutzen (kopieren und dann adaptieren)\n", + " \n", + "**Demonstration:**\n", + "- Rufen Sie Ihr Programm mit verschiedenen gültigen Kommandozeilen-Argumenten auf.\n", + "- Rufen Sie Ihr Programm mit verschiedenen invaliden Kommandozeilen-Argumenten auf.\n", + "- Diskutieren Sie: Wofür dienen die Parameter `argc` und `argv` der `main`-Funktion?" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## B. Verwendung einer gegebenen C-Bibliothek zum Lesen/Schreiben von .csv-Dateien\n", + "\n", + "**Aufgabe (Details siehe [taskB.c](taskB.c)):**\n", + "- Setzen Sie mithilfe der in [modules/iue-io/ccsv.h](modules/iue-io/ccsv.h) bereitgestellten Funktionalität folgende Schritte um:\n", + " - Erstellen Sie eine Tabelle mit numerischen Werten\n", + " - Abspeichern der Tabelle als .csv-Datei (`table1.csv`)\n", + " - Erneutes Einlesen der gespeicherten Tabelle aus der Datei (`table1.csv`)\n", + " - Manipulation der Werte (Skalieren mit Faktor `1.001`) und erneutes Abspeichern als .csv-Datei (`table2.csv`)\n", + "\n", + "**Hinweise:**\n", + "- In der Datei [modules/iue-io/ccsv.test.c](modules/iue-io/ccsv.test.c) befindet sich ein Test, der die erforderlichen Funktionen einsetzt:\n", + " - Sie können dieses Programm als Vorlage benutzen (kopieren und dann adaptieren)\n", + "\n", + "**Demonstration:**\n", + "- Demonstrieren Sie die Funktionalität Ihres Programms.\n", + "- Erklären Sie die Funktion `table_append_copy` (Zeile-für-Zeile); gehen Sie dabei vor allem auf die Bedeutung von `malloc` und `realloc` ein.\n", + " - Was ist der Unterschied zwischen `table_append_copy` und `table_append_move`?\n", + "- Erklären Sie die Funktion `table_clear` (Zeile-für-Zeile); gehen Sie dabei vor allem auf die Bedeutung von `free` ein.\n", + " - Wann und wozu sollte `table_clear` verwendet werden?" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## C. Kommandozeilengesteuertes Programm zum Vergleich von numerischen Daten\n", + "\n", + "**Aufgabe (Details siehe [taskC.c](taskC.c)):**\n", + "- Implementieren Sie ein Programm, dass \n", + " - zwei Tabellen von Fliesskommazahlen aus zwei `.csv`-Dateien einließt, und\n", + " - testet, ob die zwei Tabellen numerisch als gleich anzusehen sind (bei gegebener relativer Toleranz).\n", + "- Das Programm erhält alle Eingaben mittels Kommandozeilen-Parameter und soll schlussendlich so aufrufbar sein:\n", + " ```shell\n", + " ./taskC --first table1.csv --second table2.csv --tolerance 1e-5\n", + " ```\n", + " \n", + "**Hinweise:**\n", + "- Verwenden Sie `iuenum_isclose_tol` aus der Bibliothek [modules/iue-num/numerics.h](modules/iue-num/numerics.h) um die Elemente miteinander zu vergleichen.\n", + "- In der Datei [modules/iue-num/numerics.test.c](modules/iue-num/numerics.test.c) befindet sich eine beispielhafte Anwendung der Funktion `iuenum_isclose_tol`.\n", + "\n", + "**Demonstration:**\n", + "\n", + "- Demonstrieren Sie die Funktionalität Ihres Programms indem Sie die in Aufgabe 2 erzeugten Dateien vergleichen: welche Toleranz ist noetig, damit diese als gleich angesehen werden?\n", + "- Warum müssen numerische Werte meist 'ungefähr' verglichen werden, und nicht 'ganz exakt'?\n", + "- Wie viele signifikante Stellen haben Fließkommazahlen (`float`, `double`) in C/C++?" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab5/table1.csv b/lab5/table1.csv new file mode 100644 index 0000000..f374248 --- /dev/null +++ b/lab5/table1.csv @@ -0,0 +1,3 @@ +1.000000000000000036e-10;1.000000000000000000e+01;1.000000000000000000e+10;1.000000000000000016e+100 +2.000000000000000073e-10;2.000000000000000000e+01;2.000000000000000000e+10;2.000000000000000032e+100 +2.999999999999999980e-10;3.000000000000000000e+01;3.000000000000000000e+10;2.999999999999999853e+100 diff --git a/lab5/table1m.csv b/lab5/table1m.csv new file mode 100644 index 0000000..08e5b13 --- /dev/null +++ b/lab5/table1m.csv @@ -0,0 +1,3 @@ +1e-10, 1e1, 1e10, 1e100 +2e-10, 2e1, 2e10, 2e100 +3e-10, 3e1, 3e10, 3e100 diff --git a/lab5/table2.csv b/lab5/table2.csv new file mode 100644 index 0000000..7a178af --- /dev/null +++ b/lab5/table2.csv @@ -0,0 +1,3 @@ +1.000999999999999955e-10;1.000999999999999801e+01;1.000999999999999809e+10;1.000999999999999816e+100 +2.001999999999999911e-10;2.001999999999999602e+01;2.001999999999999619e+10;2.001999999999999632e+100 +3.002999999999999866e-10;3.002999999999999758e+01;3.002999999999999619e+10;3.002999999999999448e+100 diff --git a/lab5/task.c b/lab5/task.c new file mode 100644 index 0000000..7ed9971 --- /dev/null +++ b/lab5/task.c @@ -0,0 +1,24 @@ +#include +#include + +struct Vec2d { + double x; + double y; +}; + +struct Vec2d getCoords(double Winkel) { + struct Vec2d erg; + erg.x = cos(Winkel); + erg.y = sin(Winkel); + return erg; +} + +int main() { + double winkel[] = {0.0,45.0,90.0,135.0,180.0,360.0}; + int max = sizeof(winkel)/sizeof(double); + for(int i=0;i < max;i++){ + double winkel_rad=(winkel[i] * (0.0174533)); // double at end represents est of 1 degree in radian + struct Vec2d erg = getCoords(winkel_rad); + printf("Pos of %lf is x=%lf and y=%lf \n",winkel[i],erg.x,erg.y); + } +} \ No newline at end of file diff --git a/lab5/taskA.c b/lab5/taskA.c new file mode 100644 index 0000000..78416f7 --- /dev/null +++ b/lab5/taskA.c @@ -0,0 +1,46 @@ +/// @file +/// @brief Task A +/// gcc -g -fsanitize=address -std=c11 taskA.c -Imodules -o taskA +/// ./taskA -opt1 10 -opt2 1.5 -opt3 X -opt4 lab5 + +#include "iue-po/cpo.h" // iuepo_get_* + +#include // printf +#include // EXIT_SUCCESS, EXIT_FAILURE + +/// @todo Implement a main function supporting the following command line arguments: +/// -opt1 integer (required) +/// -opt2 double (required) +/// -opt3 char (optional, default is 'A') +/// -opt4 string (optional, default is "empty") +int main(int argc, char* argv[]) { + + /// @todo Parse the command line arguments according to the specification above: + /// - have a look at modules/iue-io/cpo.test.h (you can use this as a starting point) + /// - If mandatory arguments are missing or cannot be parsed, the program aborts with an error. + /// @todo Print all passed command line arguments to the console + + int opt1; + if (iuepo_get_int(argc, argv, "opt1", &opt1, IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + double opt2; + if (iuepo_get_double(argc, argv, "opt2", &opt2, IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char opt3 = 'A'; + if (iuepo_get_char(argc, argv, "opt3", &opt3, IUEPO_OPTIONAL) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char opt4[256] = "empty"; + if (iuepo_get_string(argc, argv, "opt4", opt4, sizeof(opt4), IUEPO_OPTIONAL) != EXIT_SUCCESS) + return EXIT_FAILURE; + + printf("'%i'\n", opt1); + printf("'%lf'\n", opt2); + printf("'%c'\n", opt3); + printf("'%s'\n", opt4); + + return EXIT_SUCCESS; +} + diff --git a/lab5/taskB.c b/lab5/taskB.c new file mode 100644 index 0000000..51f31dd --- /dev/null +++ b/lab5/taskB.c @@ -0,0 +1,74 @@ +/// @file +/// @brief Task B +/// gcc -g -fsanitize=address -std=c11 taskB.c -Imodules -o taskB && ./taskB + +#include "iue-io/ccsv.h" // struct Table, table_append_copy, table_clear, iueio_savetxt, iueio_loadtxt +#include // size_t +#include // printf + +int main() { + + //writing Data manually + FILE *fpt; + fpt = fopen("table1m.csv","w+"); + + fprintf(fpt,"1e-10, 1e1, 1e10, 1e100\n"); + fprintf(fpt,"2e-10, 2e1, 2e10, 2e100\n"); + fprintf(fpt,"3e-10, 3e1, 3e10, 3e100\n"); + + fclose(fpt); + + /// @todo use the functionality in 'iue-io/ccsv.h' to implement the following steps: + /// 1. create a table of type `struct Table` using the function + /// 'table_append_copy' + /// containing these values: + /// 1e-10, 1e1, 1e10, 1e100 + /// 2e-10, 2e1, 2e10, 2e100 + /// 3e-10, 3e1, 3e10, 3e100 + /// 2. save the table as 'table1.csv' using the function 'iueio_savetxt') + /// - use ',' as separator + /// - use '#' as comment + /// 3. load the just saved table again using 'iueio_loadtxt' + /// 4. multiply each value in the table by 1.001 + /// 5. store the modified table as 'table2.csv' + /// 6. use 'table_clear' appropriately at the end of your program + + double mult=1.001; + + {// write + struct Table table1 = {NULL, 0}; + + double data[4] = {1e-10, 1e1, 1e10, 1e100}; + double data1[4] = {2e-10, 2e1, 2e10, 2e100}; + double data2[4] = {3e-10, 3e1, 3e10, 3e100}; + + table_append_copy(&table1, data, 4); + table_append_copy(&table1, data1, 4); + table_append_copy(&table1, data2, 4); + + iueio_savetxt("table1.csv", &table1, ';',"", '#'); + table_clear(&table1); + } + + + { // read && write + struct Table table2 = {NULL, 0}; + struct Table table3 = {NULL, 0}; + iueio_loadtxt("table1.csv", &table2, ';', '#'); + + for (size_t i = 0; i != table2.n; ++i) { + double data[4]; + for (size_t j = 0; j != table2.rows[i].n; ++j) + data[j]=(table2.rows[i].values[j])*1.001; + table_append_copy(&table3, data, 4); + } + + iueio_savetxt("table2.csv", &table3, ';',"", '#'); + table_clear(&table2); + table_clear(&table3); + } + + + + return 0; +} diff --git a/lab5/taskC.c b/lab5/taskC.c new file mode 100644 index 0000000..8fe8d09 --- /dev/null +++ b/lab5/taskC.c @@ -0,0 +1,87 @@ +/// @file +/// @brief Task C +/// compile: gcc -g -fsanitize=address -std=c11 taskC.c -Imodules -o taskC -lm +/// call: ./taskC -first table1.csv -second table2.csv -tolerance 1e-5 + +#include "iue-io/ccsv.h" // struct Table, table_*, iueio_savetxt, iueio_loadtxt +#include "iue-num/numerics.h" // iuenum_isclose_tol +#include "iue-po/cpo.h" // // iuepo_get_* + +#include // size_t +#include // printf +#include // EXIT_FAILURE|EXIT_SUCCESS + +/// @todo Implement a program according to the following specification: +/// 1. The program receives three mandatory command line arguments: +/// --first filepath of first .csv-file with numeric data +/// --second filepath of second .csv-file with numeric data +/// --tolerance relative tolerance (floating point value) +/// and two optional arguments: +/// --delimiter delimiter character used in the .csv-files, default ',' +/// --comment comment character used in the .csv-files, default '#' +/// 2. The program reads both .csv files and +/// - checks if the tables are of identical dimensions, and +/// - compares the entries element-wise using 'iuenum_isclose' +/// using the relative tolerance provided as argument. +/// 3. The program returns +/// - EXIT_SUCCESS if the tables are of identical size and numerically +/// identical, or +/// - EXIT_FAILURE if the tables are not of identical size or not numerically +/// identical, additionally a message is printen before exiting providing +/// an hint, e.g. +/// - "tables dimension differ" or +/// - "element (i,j) differs" +int main(int argc, char* argv[]) { + + char in[256]; + if (iuepo_get_string(argc, argv, "first", in, sizeof(in), IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char in2[256]; + if (iuepo_get_string(argc, argv, "second", in2, sizeof(in2), IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + double tol; + if (iuepo_get_double(argc, argv, "tolerance", &tol, IUEPO_REQUIRED) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char delim = ','; + if (iuepo_get_char(argc, argv, "delimiter", &delim, IUEPO_OPTIONAL) != EXIT_SUCCESS) + return EXIT_FAILURE; + + char comment = '#'; + if (iuepo_get_char(argc, argv, "comment", &comment, IUEPO_OPTIONAL) != EXIT_SUCCESS) + return EXIT_FAILURE; + + + //--------------------------------------------------------------------------------------------------- + + struct Table table1 = {NULL, 0}; + struct Table table2 = {NULL, 0}; + + iueio_loadtxt(in, &table1, delim, comment); + iueio_loadtxt(in2, &table2, delim, comment); + + if(!(table1.n == table2.n)){ + table_clear(&table1); + table_clear(&table2); + printf("Sizes do not Match\n"); + return EXIT_FAILURE; + } + + for (size_t r = 0; r != table1.n; ++r) { + assert(table1.rows[r].n == table2.rows[r].n); + for (size_t c = 0; c != table1.rows[r].n; ++c) { + if(!iuenum_isclose_tol(table1.rows[r].values[c], table2.rows[r].values[c],0.0,tol)){ + table_clear(&table1); + table_clear(&table2); + printf("Values not within tolerance\n"); + return EXIT_FAILURE; + } + } + } + + table_clear(&table1); + table_clear(&table2); + +} \ No newline at end of file diff --git a/lab6/.clang-format b/lab6/.clang-format new file mode 100644 index 0000000..58d2d36 --- /dev/null +++ b/lab6/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +DerivePointerAlignment: false +PointerAlignment: Left +ColumnLimit: 120 +TabWidth: 4 +IndentWidth: 2 +... diff --git a/lab6/.clangd b/lab6/.clangd new file mode 100644 index 0000000..0e2a4e2 --- /dev/null +++ b/lab6/.clangd @@ -0,0 +1,23 @@ +# debug: clangd --check=modules/iue-io/ccsv.h +# debug: clangd --check=task1.hpp +# debug: clangd --check=task1.test.cpp +InlayHints: + Enabled: No + ParameterNames: No + DeducedTypes: No +--- +CompileFlags: + Add: + - -Wall + - -Wno-unused-function + - -Wno-unused-variable +--- +If: + PathMatch: [.*\\.c, .*\\.h] +CompileFlags: + Add: [-std=c11] +--- +If: + PathMatch: [.*\\.cpp, .*\\.hpp] +CompileFlags: + Add: [-std=c++20] \ No newline at end of file diff --git a/lab6/.gitattributes b/lab6/.gitattributes new file mode 100644 index 0000000..8d081d0 --- /dev/null +++ b/lab6/.gitattributes @@ -0,0 +1,16 @@ +## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings + +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.h text +*.hpp text +*.c text +*.cpp text +*.py text +*.ipynb text +*.md text +*.txt text +*.csv text diff --git a/lab6/.gitignore b/lab6/.gitignore new file mode 100644 index 0000000..9146a80 --- /dev/null +++ b/lab6/.gitignore @@ -0,0 +1,364 @@ +# custom + +taskA +taskB +taskC +taskAref +taskBref +taskCref +build +doc +.cache +.vscode +.idea + +# https://github.com/github/gitignore/blob/main/CMake.gitignore + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ttps://github.com/github/gitignore/blob/main/C.gitignore + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# source: https://github.com/github/gitignore/blob/main/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/lab6/.gitmodules b/lab6/.gitmodules new file mode 100644 index 0000000..d7dd0b0 --- /dev/null +++ b/lab6/.gitmodules @@ -0,0 +1,8 @@ +[submodule "eigen"] + path = eigen + url = https://sgit.iue.tuwien.ac.at/360050/eigen + branch = main +[submodule "modules"] + path = modules + url = https://sgit.iue.tuwien.ac.at/360050/modules + branch = main diff --git a/lab6/CMakeLists.txt b/lab6/CMakeLists.txt new file mode 100644 index 0000000..df8fead --- /dev/null +++ b/lab6/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.20) + +# define project metadata + + project(lab6 LANGUAGES C CXX + DESCRIPTION "lab6" + HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/lab6") + +# setting required language standards + + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED True) + set(CMAKE_C_EXTENSIONS OFF) + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED True) + set(CMAKE_CXX_EXTENSIONS OFF) + +# misc settings + + # generate a compile_commands.json + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # make all symbols visible on windows (which is default on unix) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +# find math library and link to all targets + + find_library(MATH_LIBRARY m) + link_libraries(${MATH_LIBRARY}) + +# global includes + + include_directories(modules) + include_directories(eigen) + +# include own targets + + add_executable(taskB taskB.cpp) diff --git a/lab6/README.md b/lab6/README.md new file mode 100644 index 0000000..89908d0 --- /dev/null +++ b/lab6/README.md @@ -0,0 +1,24 @@ +# Labor VI: Abgabe (45min) + +**Dieser Teil wird über [TUWEL, Labor VI: Abgabe (45min)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse15)** abgewickelt. + +# Labor VI: Praxisteil (90min) + +- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**. +- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint. +- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben. +- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben. +- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren. + +--- + +Im heutigen Labor sollen Sie die folgenden drei Aufgabengebiete bearbeiten. + +### A: TISS-Feedback (optional, ~15min) + +### B. Laufzeitmessung fuer eine Ausgleichungsrechnung (*polynomial fitting*) + +--- + +Details zu den Aufgaben finden Sie in [`main.ipynb`](main.ipynb). + diff --git a/lab6/compile_flags.txt b/lab6/compile_flags.txt new file mode 100644 index 0000000..5e4fa14 --- /dev/null +++ b/lab6/compile_flags.txt @@ -0,0 +1,2 @@ +-Imodules +-Ieigen diff --git a/lab6/images/benchmark_lin.png b/lab6/images/benchmark_lin.png new file mode 100644 index 0000000000000000000000000000000000000000..43feafbf9dc745c11e033b28c85b352e0e9bcc20 GIT binary patch literal 38162 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�tCP~ zAm71RQ9lBF_OoM3<@eIE*7&J<^32tI5H)vkaZByhLR-0EUm0c|StAXR0KMlSX$Zn+!(^=4*iSYF zyN*tX0?`qsuDA62Lhx2v5yS^&0GJT-J2D}{sj{*%k|hVK4z;8M`UkTX`3ElFDJ1m= zX}q?!wvSM ze$7Ndgm3PR*=0!-6M88*`8Rwrzu_O$QwLm7Hz8StXdE!wfMzy9uUkCvq-cdKm>lRd zN1zrTONTa`W=@J{1X~dAmQD*g_m*&0`WVE~e zt|{9<&pPwfHqOu=#gbCs>a#iRGWX;k8=+svrv`jLq{XZs09UNx0>JoWBjw_TE^DTh1+g|npct^ ziYNl4pQEUDOS8H1z*_Lhb;*NINRpuq5^qiF`s1s)`#Na% zw#S&+aV4-kMFr6R8A^dk)G9ZSj=W@@@lheyIoIK`wz&d%h`OKi^;v~~3Y3BYq_W<6 z^>8eN%^45}A3hpq%XMP@d$~%zijy*s=)cPZ|7o%}I(3M+_AZ<|x;AdI_7RJtatzBe zf2ybdMv&^q_^4mb#1|Xt2k+`Wp|R(_Cy0J|zv959E zE6o#KEHBY{%73?o9dfNUTz>fO(zxXk>NB|KzlF-cJky=&@S)fKCv4FZMGWrk9$DSK z!rF|{SFdnyqZGeIA{*o$QKBdReE_zjGHFNExZSxcd#Zh*Tq46H@CSdr|4!KjNp-V* z04L8P_X(J=uSUKE%XMdz2IYgemZlnsMF@AO+SL9>!~RH*pE{Nt0#v9wd1Wv=^r(OP zJWb?5U}J<}?BR4pI;ifTG`2?{D#5;2gTa-)_TN*;=&93g6bN)BJQ+cmDEGf3ft@yE zVu?J3+J8?$aD}CL=XlmbGw_*suJXeyOBzf#pP|k?Sc#?3_wOZbPMGP9ZJAfT5yb}T zJkT&YC=g#`2YEY&fF|bOB0Q2ySKc!1CGDEBNeQb`ZXQ)O+9PNLbr}u{w%rE!FK4eu z+B>q1@zF6}abN}ZAthoeilq#Y1$Rw42l6G#>1Qgu7;TN0o(zSVzxqFx<#yN5p^^j> zU&H#24az=WjnqqpWJKP-@!#=6e}S*S>GfkM?hDZnUNL8;5Y~xi;d-V~#Y8EF_wS>f z%_uHBgRLu)F+QrB8ZYh^h<~iEg=!80!N28`y=W5Ypt8|G`#9M0c5t+^5$Vk|wtwYp zHsF5dicL|ED;ddfu>fvom4a~F5LmX9~O^O!;r;<41jnXdh?9D_%F@MD<>B+cpg&{Whkd_KlsgDKY{n9x3#GGs6v9~{{&wH(%>#l zuZTkKRJ4x~vDD|8t4x~F1XT4zqc0jjkeX?}t<%)qjjN%d0f(|$purle7t&)xpKNGt zy#l{gudwO{BdV+X`t93K!7r1OlY?^~Y_F$9;EV?L@pHJhG?pouPa{11vNNnTk4Zd9 zCgZTRLvIzxTn*O&(g*%2Ef`Y}8zcQ*slI_@WH6AwNgVme>h(wF^2E| zB>fGLd;og;5VEuIr*!397zvfo6IpqAx`z+@S7Cm|WwXAU99My?P={UE-TOq8<`hfe zEm1qld4E-{?)WbwVxoY$>bzDq;yf3^qx$VNB?{8BC}$U!_}ZS%&dyR6h}O!>?;J6f zsWSuo%;G*sWH-bHPc{o;LSEVw2>?RK#1#&Y>YtS+eD(dMv7EKAmV5MZkf^-^gR+T` zA;N1*B0U^jO%9Mr0gN?+n1SS;AmQv#x9$Wdoxt6VBL_y*F>(f?kG7Qb2#4M>+N8Fp ze~`@sro*KqNFRu*J`ca<4Ov?l?@1YvYis0a&F~5w zt(cvav0OKXP#}^Mc>+pjHT1AQyl!OmcfJKmMJ37Abv zL4rTG+my-+%Jji_ z@78H7Kd%?HFBpVOcYl9)1>p{b&W0Y*CU4uYKe+a|jy1&{E7G6A$H91J< z7{wBK6#o;_y~&`2B;p}FiKN`YOn;L{{sV3yUhb~(|3dodR}tepgR2!~qR|{=I`L&r z&)9J0iw}YCqPKX=AL8 zrxXMUWswLwa(1--Y@ZAemeoYnL!hug9?5esD?>D|D#QU)SUw*T_Ti!b5rm6j@Q8=f zD)L~1>JADANl}hnGU$XX^I%nbcEOaQORnI-Tq@O>e3dK}R;U3b9 z0zX0wyT=cQEUwCpq5pxC@y>Sf&TR7WM&F~4WtSNlwdeQQ z;H~I#ViRdQzwe4^KjUOx(fpwTR9YEKsfM<0>h4Ag5gF-g)I4DZKYVd-eaSogcLv`# zE{&dP(jy#+`W{N7Q%t6eKJlMz(dA@wI8Y)g#+P?D9xa_1WFyACtfl_-KJrl5IZdT9 zeyo$d-=g13jgXmY_rC*ztOgx&ySXmTW7PG0J@X?$^bO>HE~~!Wq8UNPn$mt+QJ|1@ zvNiLYQmNTZU=ZM^b)+eG+FPYL2g(L?4?6zO$WxYV%*p~fC9GBWUKqs;nZ7jTctr%d zxjU@3q-#x1zgi0KjgGozi>(TG6Hn^N@)O5?q0&s~^jzWvmRFjp^icMV$Q$~ullgiT zSISjBHIDZrB2sg8>5a!ok0wIDNDiZB=2)3^3{h{s*emP=81}k+2x)`c!(|!f?&Sv@ zDO%@Dh@{SpZ;PiV`sb2T#YOc<@{;~aSo9WZM>6F~19J4XC4>xiO(zIYs6z;;LAFC@ z+f@Z<%{*ii0e`+FpIV)to1&KOT+1uXdU)YO|CdPomG!Q+gcZ5eN%Y#$dE3GL47%6b zi&6u)%UOUB2+30U6GFq+lPH$>PZY&^gq;Y$UyS~Tn|H_Su0?sht6$V*Z%#C$ny7%}-t`qyQI z@gw%LECF0^=q~BX`K$U$Y8o1ma1blD`UbFt2U^y;9TcrcJ29jGUS-yZ zBXF61zDnsCcp9+i+3ixWYKO9Co{F}A@@Rs={zr35bMa;DNVqCgPvq!PR}0oXa5 z@V(FKL<22JQPUO^AsM>Q2k*6%;gYoQr|yKf(X z&-=Q_R#&&|&8-5(->mW&(@6Q^wjc%r1Ly{nPPH26hcB{ZW~w*pIy<$XeT2`?S#%oL znxQsjiei0pug|tmNY;KYj_f=7}`|vMU{GZ{**m(&J5zZ;|sCtfCqw zhLcmGT?f+j@PAuQFl)v;6ETP@6!%soU4iq-lG4&3pYP0s1bv1s0>{CnBqbZ*JSzgx zu;cYr2ubRkDIl(A<5Z+KKHjTqPUFNLnD*5A=gvWMea_m;aiUFbt=~;GPj;>r-jN&k zF8{+RZrmm9a?#V8GY!6ZQAtT>VK)w?*r)$H)Esa30}CI#adA0OS9C_XeBJgf8qH^Q zE#_R>`X=32H|NIQZ`#g@9929gA_+{} zY?-nyI@&fGEY=;-ZG6&bO7WB2Jfv(PkkEwzrV7&Jzu=1>;Jj2@XE#)^@M%3`aZpH4 z-S#n|C}kz2mH=9q{iMkLqG-@is~EnY<8_I{lYGU&FEWg9xtujM4WTBYC-A^&fLmdazAbt}|+-UblYw=ml!G{GeRPB@G4RW^ZFd2`9UZ zQ(RgsD%0rUJjON5C)(?BkUXFQnW068?#Q*11O@sLVQj>k>UWwHG}zhKo?tS{=NrK7 zCOzZ5;*EW?`z&FQE(0IegH-*Ss6rpq62JHF(+8Kqj0yjJiOL7yZgb{lOT^h~Ju!Ym z5N_vY{St$IdSr^Qf^&`%LmmI=-TnvtzgAXV>P~ld@z!NYve^MEt4gL|Ho!L!6u~J6 z@W!KJ6K{(8i_Y}^7;$!MrnJgjwkhss&)0AmQ`z|nBs>nj#XwNOP1Vj3mr#`M<4g1Y z9EWSUe8EH>*jk-VTx31sQ8F+g;1G7g2~?!h2PXfc@2`OWSnsGOkLGK`0)g~f0 zJ?8WjKH#*<~KtAR=>+^nJLB=nVm8-sI)$vji;kq`A-p2Kl&3!XPh z5HVqOqJwX}X_3w~a@gc&oUAZ z3lK+z4m(#M8Amn0=NvyxI+LNC=S`dDTTvpNz_cQZ4Z{Hr+!`WMlVw|dVkIHF}jZlHSAN#Tzug_kON034V=Al|Pk{Ho{)1E^KXXx$Ny? zx%eu#tjyv~TARyX6mm{=pw7b$jw8dVuoaAP@Dh(qk%2lARUoPZj-TTt5n=kAeoe1) zb&@NGaN2BnKn%-q{`fi-6}76HC$nohIZB>izK1R)8@?_8h2=FHAh5^f4Y(0b3BuQ* z*wtY6l~Pw?KEJ9U3E((ac+GI4-vl+LyrrjJ@%P&=HE8b&ipe!D5mNkgHf;MJP1R(> z5`0wfvR}q4|M^*UrAA#KxW;SgG5$yRqM9vpC`Jqr^(yO((i;p=Z4tiRh#=5=ZVJ=u zzuB;!cT^EQwZ0>sJ3Js%BsfDkqhwR4H-jy16OrwNb^#%P-M>RP=H@%gAK2O1Rf`R< zAjHv~t92858%xwb4J4*-DN&)Zk+>*{^u{By_NeHUGch#&>rQX|+pf+I%*KtI6__3Y z;`_X;M(CvBgrlq?(0<}Xenlii;zybJ2#_{T4bQn2TzK#Wq9TH{=l;@?bI|w^xe(H4 z-+LJ3H{KJ@e%h4~Ok}mBg=Tk=cEQ1{vvW#{o;2^*aZ0-}<>G#Ovvy+SpbA)IPY*kJ zI6>p~xez%d%-;Jy>uH#j{_!<8;3sYrnV{HrNlAI#g`8+6zB-@Wb``X~&zu5en@sTB zQ8rPPF5=(YB2lXFEd(%N5I6=p`Dti!BJaEIG~}D$95)NABQ#`r&?O9x$L8EC1kGy( zzGNoH4OVkMUT3HKLeC2~X|y;QkNT!s-3Q&xRrzl~MQw#44k3+P%B2Q?Bj~TE8_>qe zErZ_um@qeipadLQ%K*yl@|BU&$Jyex)qT*L&fw?=m#p%I#GBw1g5WY`%&wb9t)GcU zJq`I?(^HH)1TaKSKJMr7Rr}#Rz6-6HoGd@YHmWP;x>f(YbSY|i=$m3kx`ux%?k5k!w zd{N_BEWOyIHpVab#*cd|1ygZxaTY%sqaIL?a6=oOY}Lw5Q~rLE{AGHD>fK`CsSV{8 zdJxs^ugAM)in|X`yy|=d&_4!tO@A$e2=a-*2`aPz98Jf-yfzIU;zJG3rNs}kj<;dX zUq6DJ-BhbS@mQJwDn*XleMeCXtnk2+4Lq;bJdYBY-p)s{Gy?aNLJj$)Hwk?ep<;n^ znLcEO$Q8hF**|=NZ&-oNk~VQWes}!gz@9{h3vXX}pr$74wl?f#62EJM?lols=4gFg zg(NWBKF2;CZXfA?LU|_#%mscl82U;7it>AD$?o}y==Vown_MV$rR|S$A`g(eNo15# zQMGl)0$lL%StSc3k8XJb8b8|c72dMjEqU!LDv$su4!)^n5rh<%N-3Dm&u6!-w_!Rp z>*8TC{mb}qV~OU+kP~n15{b{$|GDt@57r&blqHVNTNyu)*x=Hd%}~sNuoD#xwC+;V zL1&ON;sspT;cJneWOJnERb_;hGyAj&3$F_FW=w^C;$b+f9%IjTvNSse#hyt)=G0|f z9%({!)+wCKID1$N%^j=*8TN#H-Hd^GaDR|?06>zfY}B~`M;pXEw;YQfTPryiRN=qS z{_GbSvOdFlWA?iEr2qVUcjPZK%VniCGhg_GJQ6R!=A-@lsb^T`bU_&+<>ecsGQN<6Mac*iijH+jG-fVsf za|owomKGL4(+Y;{+YWBFYRw6i`J~C)FuUP6PX}}Z7M(2v_G=`0{Pm$wO+qJmu|*Pu zx`|L8cimko|8~YODfqjGXQRD_E{Lx=1u@8)-^p-xz4AtCM`F;y&XjK=>ePle5d05N z;po2SaTuIo;sMx!@FtlQDDQl>dV(%61-v?ZDZ@(^ZD% zjK1tZ(TVL6-_w}L6azXLX#E5QT(!OjOLpd9&E>ydN2~wP|Alrz~u6-5KE<~&e z5augjgdxRN@=Xcpk)4718#f0_9+;HksWTA<<8pqP)oi}g!J)rB?l&?DM^njzW=@|Q zO!d#J8yw*DexMRh;wzcpaUlz+DL^7m24AbRe15xM!obO&2HOU$^&ee8=mJC+h%t>N z4?yJby2)>LX~_==hfwR?;o}=$wZHLDYoT&Ep{b{b2+ZSvp5_gfHsyT2B$fOwoNuLV z%h(rt=iN#d-y2u@Jl?tmdD6fLUkDLX$TjkCKQ0XO!>^~r57sKa^eplL0z4<=Jd70; z9sL4C0fzvh7M7La2&-3e zvaec6lYtD>t7a#v*QvCdX^hTw3?jpwCS0*ttI%&&OB~XcMFQd$SEO!HQ&S_{cVNRV z57E=yXJ7~f81WxFu1}msuGmltY4ifRy1BWj1C4sSjRgDe8vvPsc>gs*PX#pvu+tH1 zFq^&Kir^uKLN1jI+R7l^P*D{;8A#R;Wo#Y@lU@AMhz%8eAh z0fjb{AJLY_!(p5Eie3O@a+&PO@=!ckne|Qh2DsN46Sh44&P{hbceDF@`|0rH78c+} zf#Co!SQ5bFL>BBoK<3;%+#AQs-n(B4f;JEtwE-8c=pT^_+O6PA@y{ng^x3oM@kxra z^_==*mV5t$(Nl*hyUgGm-9L5cxo;6|zEn1ca@-tW*Ssjp_=EVh3n ze_Ajw-dCw%v}(sjQb8iaBy&DA^)Gm{D1)^d9at}ra?$=t3xz`gAszuHE)F{k2$qJm zUSQDRewOJ(eu`HSAM#V@$EB3Tb5k`z2M!x8;}KJeBH=K@nu|HM|?TBzHpE@nH%o zj=9a*Cc{p|GY`C4kmUyceBe$6B(&!U(G?*c!EU|_3^Feup$eTH>H%uKx!Xmq;{eJa z9$sEKVD?fTn3|iLS2d|#+!XfEND4fHKzp1{<<~w8zE{-Tg-uwPUw%X8&lSK92nL@*5zEd z9bB?&8K}f;DJY^6b8-!jHsMEZ$xF1ly^l7rR;oDFW1$r}Ea67&ODXq*jBAwDbD90~ zX~fcp`)D(;kOZ)zqx&)7?no+=bZP!P#}{$F)pX~tFAAN{F; zm`U8f$DFM1Ac^rqLmCucX(+d8{R@WJoawhLi`6PcqO{FV5i)>4M8yJu*h79K4Mx3D zZiz*j5@zYeRtC50Thd#K=LYeRlUu=NIBrEPJc8@3`97EaHkd+oX;bm ziQ0^1_WnI00-1c-v7oDgmn%|J(ElT;r=@Sqr}#mTMsvQA2c2Rfo>y@xJ5=?_+`kQ0 zg=mAwj)Jd?=zspo&9(=pcKtjhN>P&k5oErXJX;34&JEelf4L3pY_lw0BEGa-S=5UZ z?K%daV7?7E==IXco<8!3o0PAhoGPvV(Gk?L3*r9e-!-C75%G;@G4)vbVG%C(HM!E$ zR*S$n>@Rw6v6Y%Oh!-IXe2!8#wu>u{|7Iu1;=l*TowpH_D>Mtj#9y`+M8VS z&LY+o9>PGC;Jsq-n=PCj_UAOHN-TnteWX#hn!&W8oJ2ALIA-6FcM!Q5 zg11u^zc^rj?#^5k&*KAh^S5TyjQNI?N-ArW;xE78kYI!0 zt0JUi5Df0qn%R*9wYj?vpL<2IR~zRq^KCZ5XtT;1Unsl1)l$V$hJTj2DW3)Z{Q!9Q z8^yHnmjm=rxC4GoDtSXrp+mnvnRS2y8l|&$_lMQ|%I;gP*fi@^g}Hby-is{WZGNRI zuXmI@bc18Z6i0s#(`1m1J(xsB0$rbqD;uD@0yoX_0lF8B&)t`yD4^W!kK%TfjHIJ} zvJmnd*2&?{WTO5}c9d1Q{XK>|9fM+x(CRZr9(a_h>)oL8NSeEkq&}pSMleiey*OmT zywZQwX?}wn9$jxqPL{f2Ba{CCR}wQ{h(y7a(3qvq@1WEzj}OI*OA8Q zp`OO@z)5{_HmUDD9X|pAcZ9HGgq4Vt;pEhuNJXDq0qsN4M>?#GKHbNWSdnuWH~VuQ zq>L+kQWeC@l;@T)=AHVJ70H`dk#z%>% z662z@Ol}Id{jU2D2gEvmp-W#WL9HXX{h5Yz6@iypY8qf;yO-9VQswhq&jV!b3UbFT z^4ZY61!Z9y5T#B-=y}F}0nziy8Q#~!T_>T_4S6`B#hJvblz&O9Slz56szWFba_|1V z65v_i^EjhznzsLB=(8qnFoz3{&24=UsP1gN@z=fQ93oogM2g|s)3&t)*n-saCW(}#DWdPYrlj`=Dpf}TR#k=Qn=PF? zr?kY3TD9L7&^~j{DILC^2S}>TzYm{@Wj3me`6%|WS`g_>niv~NMk>si*Vcs3QuwEQ zJCp}M4 zgL_%QDdBg9CbRnhLYI=@evbq>60OCG8JmWtt1-ckR)xc}1y` z3|q;cQV?7P4y(4NVejyGmv_(Gs4q7MUQ$6`0S6VtG`zrI?pIFk9UesJ zgq~dOH>hqV7nQP%E5D{q&$q|Wy#OCa8LNVl#>d}hpfMktytEZ^Q(93p;vz`9dHmHB zNo~vf_jtzMM5AkQ*0Lme6X|Zg#|fR{@hfeOS^gm;o;V|7 z8QY7Jy_ZA^I?V!b7iM^o~AFv_afIz>5EYd2#M z3qf?1>7hx0&^fUIVs!F*INsY*LCRrr&Gq3V2exon`G1hx*6F@!J1p^y88l^(plISp zp%OwfCP5FdC6t1f9(BD@IP_@aP0JOOPJ8mTB$&C_btGBZYf|Uq2t+UyXA?TeJ!eg} z700p5=07hbqDX#Gw$FtILn{vYxkM1g+Qqp)^^?`E-U8Yp=Fhq;Zux4(wlO86*&jiX z>{~_%&elD~`R%v-+Z+mh%y=wVkQ}!D!18?X@5MWgaYg`-=aiVx+vU7;@@Bgtwmo`$ zUeo(So=K{gDI%*K!D56m1i92G@Ri__eZh*1cDwIS?ho5XEWRUZU+w@mxraM?RqZN z<2K|IQ2UBfe$bg%G{3L!t~KT2oXvpZQ{kP_S=8xZ1Mzh`5GI=CB}OQs_Lna~!-B{Q z5bR&C21XhcubD#`xf7Te$V2_T(#JM!vHK+EGW8Yd44DkQ4FELnqC@wkIKPvAUnXf*V5`0|}h8LD&$r1iprZ^&w=g z4l*2xu@#7UglfJ{s#Q6+#pOuPg3ms*wB6XT@OG49wE{hGC`1&Bo>$9qr~{z4;yw!@>bH^ubW2N3i6|^1Gbg zhx>Nod?o`M6_wzS0AV2tGYQg9p8^4RVhcL%`Wyqp)VN9Vgxd6vwXca&TwlsN4=;g3g22I~?daIp?8*uvZ2*=!w)6dD6v7{A5BH&@!DT@sy_&me z2AHbsa^M9*U;8^eY|}9jkW8oZ!~^HE$YrT?XL}Q>ff%Xzd<)MTDLrVwLkEojj zI0&`{5;*Yw_N;JNBZtP>v-AOk!zM|c(2l#}f)&4PvF!4y6>B2CKCBU#mG~K1$xS*O zNG~6$2(Fd_d^S?5$-y#p)2j+?&#agL;i~Q1E7E6$c9&(Cz`Y4z%`&qw5x`oX14W{# z$r}{UeYLKRs8E{6(!lrw%dBqzN1VWS04Z4*%%_-J4Cizofqri{NS(mMz2(P`-(QcV zUcPJwkP_5oz-HOryV)`T2@Z?)>da>N1)Jj#nN!4hgY1myKqC1&dc58G- zaILhbW+NU&2?u^oZEW4j=M+3NZ(DJX|buxe<(SjF7;t=yxc(;p7- z$zL65;N69j$e@l-`g}Oe?p2@P&@@Mge!{F0^Sy<7X7j!woe_O8^v+6G=nB_43O)9Do@Sw>Av~KCiOpj)x z1kjj>cpmF_AmE2- z8F<3L_5|ZNFd%LHXSDiXT<#q&iQKJM>;yH13n-wH>i)*AvBewBXvpBA%RksCXRqIy z%vwGKUdJc)&zJ1*7HV-e3ct4Dv<^}{WrzjF99T7o3Nd~X^a9ZBpS%pzLp*$ZF?nO4 z?`|C)z7C9G#BCLrPS*DJEgc8{5qBFcu6ZHNUiOk6RSaf$RP|( za%g5Du`a{%zKLSki2%YhzeL*cTF2&3ZZue=gRv8C5zaem;6B4Wp$9)+pxNqp?|LykLcY6}d|Kc*#CQr$fr^Ri(|R{R9^HQIc$v`4*Y^bT znlunDHWPQ;4qfl!q=+EaDXy6;bu48N_Jx58hW=Fx6~^X4YQ5CR&Jq9L;+z3CC>4TgfWWoq>8RMd^FRK5b1uM~3Ls%;m`IZCe%dqi@W9spQ?6{XYBU zp&}aL{`=h5zl#de{r*NlHoN#8I;t%Ejq*@z%o$4rGh-svd%@?;g%^LjjHDW+CZG_# zJH()>hz%3+HW~Q1ibQaQU0|NF4GTZc+iYt1t76LeW-v>a;tVSHoa%zmXJNrSZl|oM zFzYbrtYLxV39@z%#lNZmeL^M&bB5&f4ancW{JVA&lm{4IiRaZ_l7v_fG!)M&5?ta9 z?nQ1vR#chbLWPP~6ZcOdf@l|+F)#_cG9dw8d{kRipim^d$_wX9J$^gxZc~#?co=2&Npe4ul8FN3~xK<$CTu) z8mts;zei^n&9{Uo*yl?a`}d;|9+8~{2i!1(t-u%3)2Ik#;Qb%^lnv>(<}Y5WA#6(~ zD|a7Qm7;<_vJiH6`n`>5 z5X%HbZ6^?^m=19;F1Ief1_CMbU%}Vu76}NmaJu(QI);`IrA(n*3@)Z%G2qiKc?I?C zYosx2@_>G~JjE}I-XC8>F4>fer?C9}`etD%EY0*7C8F^Z0Lt|L0EYKUkx&Av7E+UY zDVAHmKe#&A+g^)N-bEF;U<7BWla*Wj?;&z__``fPA7qVVug7w9~= z!oEREXWallBFt0-ASOv}hME~K1R%94_dVity-x-GSSB?(p+jLk=C>fQoSP&hZwHx95Dtp=4EK+@RbG;le>F` z4C9nabQi=lRYZGyh90F}esQ^7G`!k}3|y3w!IGJIARsp!2ap%gxO|GrYHKRAB7XZR z3xJt1P6TKL7!bv=Y1{y9c@zTfN=5*WNkrh|1>x6^7|2`@JpptR>Nd2AL)3grmAw5^ zTKiKMf2;L#UQbl%3i{0SyzBXe1|?I;NE^+Jc}1j;4*Tf?2#5*-kiZJ=w_zh!`mz)$ zD@bMHy&#nTd8;e0yHZeua1Cg;?UfxYZRd8Tq3+Kj>)0B3s8K{iZYqbp&S;mi+gPy| zt{ujeLq)Fx+va{$ugA^!E8Xg`(`@QW0Gq?LA$y_3(Ost?X z)W)OD^%B1ip|cwP0IU7wlpv@2VYPO8QM?nhMid;fHH48cLxP?Et1F)_C=OBRJB?4T z);kT9bX}Px*3s?~Djuk-bgpaML4XnwQdij437tPF5J}@kj=;-TRS6JIRZ0?&d@oV! z^trj7yzWl;Aa^Jb$ZTr+pb@79tlrVP8;7R-uKLD<^>JWs9n50-6>yn;`xv3Wpmw zZVf+^@6~pW+!jq2wPl)D3N=^E*eu#fE;Cxg%z+cirvNm3bA>9#gPUSr+27>m}$ zsMS4l2v$M*l`JaDAYoxSKll27^M8?yl-DVXd!IUeP;IMuZs26jr?As;z=r^JfX5V^tBb`uguLJd0*4WYq_>BSam@Jp@`H@?%c zb*ObOOeBS>ygrjh%D{8yza_JP3Ebo3!Ra!t~U zd~z+hcql=py#=LKW*3FuPuw^DW+<}7gu$n8@xaayFMGrmQ^-UF{_0e@YR^X0QdR2? z*gg&&b7x7Rch6wYs(QW_?38|>2kY(DAD8+UApk#)<+8D>bvgY$h37~@2N2zs?28o_ zAvN3gbgnEIvVEOzqXkW0L5K%e3m!Y(x4%7PrjH$g;NB_2vK+}?Pm)MY>?M}^HCmf} z9JkbSya~PzZL>L8%svIV0#N_FC9u6?A`BLauNC$gOmY0z#P>`21kPi{4G8#b`llp$ z^xD`BAWg8iR=p*1bYruqd{X5dypO2$-KGWs-#Ir5XFqg+bPAhqm8Z0}c&-v~PSq34 z&S6KNIP7z4W$xqgl@fV5`!4|r^A^$`d$B@aNwjzy385i#CugS_3sg$_w!0>nAW+q} zDlW3ip2rnV*_QFN^@roVbCDUT-97Wpyx)mnN8+Vz9pETZeA#d$0l^Q62xd}+KhUC( zy4|hm1sfrrEq8pHq?C3^Ox#8h=S{*790^(qHlzqkeX%3F`CDsL1oyVNK9^lEQo55Q z>p&z##nmTtwI>o6Z0HK0%o$xDE&7^N`qjr74#HMKbT?7m8P;?wcvMOb09#{Nm0B+z zZTAl3>T;h@o$hRSAv-mU>5>;+T=IR<)vETkdht)t4(e0uJdV%)nd`8k;kxj@bU8}{ z*a0sBXLGwPA0_=pp|f_bqf)6P#=F?bga8C$Z>*UR$UYVT~^d{C<}qINEZDx+Xsj% zI?#HA8)K%%m1fQgF&O#NX0S2q_gE)bL!Mqhqqqx7@?-8jJ+k7HTYZ5m8t)}vugv&; zuwOR!qs1cT{~&I1oUw-79l6cBs0b9Cm1!r|aWv<(C}CAcSM(am_VZa8IZ}sbYr5b2 zut6D6XL5Q?%RvjU75(Z<7li-0f|vdZCi+P z`=T5V7&5dv#MSjko|ogNV4{Ii*`LBE#UyYzXl7%`288#8HoeC-fZC(V)YKu7{vjEz z-8l07hBmIYVOdo4JdYRXgE%P=(*wwlKU`?g%>e^NS|MqLT^oJfbVi;D!IReadpCyJ z-FxkBnf_c7`tFWoxGY>OhHGlLB>;JLipN~~H!s9D_&wGkDc%H+2vCHB+#^@ivN@_I z*q#0(A{Jau40$YRX7Yg0jU0w=xYSh5D#D&lmmia6^9hEF32q1P8jqD;#3iI>fL*3s za}-_A{KN$n90)1Kdo72Ln#*&Dj<=mF9A!h`2?(b4uIB@Rs*lW(2Dw0TkXPuV;-Qrk?x6ZljTYACJEmt+U@c0AH}M zxL>yFi+QLyp%@kgi-pejFl@?7>kI8w)+wJ%=`0K;e>lVdH0k3+**d83rK^jQhLMRW z$NSw&^Ur1Gox{|%)O74ur5KcRe&7h|$RK^X2MhgsIDEZR;Dr!}HB*Z=EQW%-mnE>hPg+ME8@IHNO+)c!c4Sdebfohld4@e@73p8+Ntd zGU!dHe)79+UkL+al-oq##MQlLQo}pOJcPW0&}ppXe5dw!QY+Hhb;3nnz51u$q5FPx zCi?Rpg??$rTTO4A3lB7OTs-V|ZxUR(1kNOk+SDUNs*&`m=Bfs2d3&25(G3E(F$z8w z_h)R`7cPp~;1E}-?W~oB-|VlUjL|UMDHs{E#7hB8WYTPEJJSS`rWzZ3 zMe=+1ILh;J*(a->sp)@n-WU|ro>0hqC-wXyD!T7c^Fz%T(>RO@r2T&<_%GF&u9;;iw?)O74b#szVzc!m z(9Q1}CmSZ>+%j>v&=rbma35tA(4t9jU7HI-9uRQ9p^2G{ob~2AawAzaqpS_PCn9s$ z9z1`sq=%(zRF9=lCeFjUF2ySx2d1I%JuK6Iu8Vwm!0XEx5{z$RYKq$Bw)kkhrCTQN zo|f>f%D(UDFXl&&-=yVSXYr;N08)-Qk|R8ZoG(c0Fy~eN-XWq)cB`-N{{{~q0XT94;YjI#EvIo+f#dD zg3%-SDfJ##=XySt?SnaK#f;`d>e$VW8Eu+e|1P$0WpK?EIndr&Hc3x;myTtmdsS{M z;c05EZ?dE43&P}}eEeC$A1JF^ODOzMmKl)qr6lQOml^s_3#`U+J+nRT<~5l1cm?xcEcNho$SR1b&G57XO-HWO zn<$ngWbzYF%RFu=Gr5MEyUu*uaC|pMIPz)6_W3$LWef+-%y4UnsFc&=5j|#bDbMsP zxq2_~^khe4Vw^j}63@%;(CO3pShT77e6Hxg2xBh&@}?tymsRiTa}!tBoFCzxyt2ln zDo4lB<0g}n_lMHUjcXYcrii4;A8Qp8dfQ~EP39uH;=CT+2pn`GPD zr_J@U%Vmb=^bh`6lMTNXVO5uoqi*^m)ZE=M{)y*-sUH30=U4@DLM__JhL6_#NmB#p zB>$4%!dK;8+lu9BnP(H$rgkq{dwMY8W{ zlCdgQb{NNc`?15E2d2{XjHghLFBIIMmB_39D)FIpc73lXT2Aifm8-Tj9*loD&{Oi( zFDA-9Tdz;LKl#1hWD4!Oa#y1{kDyye)A76s>+)>umkw9NIeL0fqB7?`hX$SN`=7`N z3`--=T~iqDwlJ#*Po5lAQX7qN)_-NtruJG`o1htey13nlpSEVGRVlI^ky}~DNq;Za z^YOrdtwqL=p>N;>hjh=clhY0SSL6z6%#67>xt~)qbHnp-d#ENRwesukJutwgH}!Os zSC198bQAY6@Vy&?7p~?jk)kiC?44JYpP6gUE5rEG86$i=s+9Up27%*K#m%WsU9%dq z`!_5+`G0-$euPaB?dbhMompNzUXc+^B)O3%+3lm>G_{ZRew(xMXh`CU^fAuD-_z7# zNm<}IGCn1b34dK#o;yy3Nj`P-qM^#s)>1taYtUtDednoUaig~Mmtzr|RqN-$y_M#_ zrFNDpL$I^^^@%M*g;b~Sl7iy9>*Qujs;b zIo@em;t77dma$&jw1JjQ?O!R#R_-0#ANC{sW!XG+<6996kMhLm+}Upu`H4|p+YTPb zVLRHIF-^ZRt_zb_gIMZ$8&?|-U7v>k7>}9p#(im?f=+ziAHiHnt+&~CCkc963OpM-CSy!Zr(_P$qEmfP=pOO*G1(ZAc&QCeBf zJaMuPp7?>0ww9(f6h3m(J7w^su&GPAn0PYnb?kek+)SvPd?t&vOwnleX18j^>S--c zSM{|0n4GoA?7ik3VTX;fjhfcJ&KmQ4n&Qx{GqoYLf~t*A$+QziWu_+C-F(ueM=RPW zoQ2I(Mbg7Ex(tpt=7bf>mA=EnA4@~(AHTE7{dR-(1EgJ z^H^4NJWHQj%zC(^+aByRdTn*@@-xooD+lM%oOKG%#O=&&>wDTh0myHE?4NfZ@9!w3 z^ug4+`zwAPUHQ!9mC{S59^x?EYj#ivhi|&SLSTAmkjtds|K{hT#!`g+i_b9=wNeKHY@fVx(2mPq3TEmm(}d3%=hyQ0G8Du(elXwqW7#Hn zNT1jHY#O~jrS)LooN8nLVwG}l!|75OKNcm6hCgjdL4*JYsq?NpW{wAZMd8x}l8pi?N+d zK;^yNjM5pGUjNs)G8vB#9h^G8x<`w_@;eeqALI3>4k41jjwz=qS*i1Ku&h0Z3oLZ> z-{LMRJ1DrNXMo#u=57_sufX{0%=5$W^`t(%$>)~`SNkT$A1`+M8gUu!?PKt%Z)P|g zV}-rj#at@9LW8P5oWn=vUc(p_WU3d8#`y}oUTiJ@aIui$W9xo156lL-7qerhVh+j* za_8R~|EihHQ7NP?IwoMQEOLR^eM;OnykTe#bpaD+?X=gJn>$Q@;`%1suI05 zQ^l`2Y^qduR@-c1pYEg`db0QKo{U$|%d**!1${L%C`LscPM8ps7kSR0SX(t8x#rc| zU3b@b6>1(HyJSA(=QKF1I>KqN$2TQ}{|zU8y-HQxby>v@qr9mvp88(+F1D{uQz%9zy4 z>w}q8ecWoXk^cScccnNSt=6bEv%o(Vo3iVwMt|^F42t|i_=(p9UytT$3^NE9H`vN# zSnt+N43~3p(8RjCI*Rny)=IyXN*a+l*rlGjk)_dz28K0fjq_%LIeI`VrPIa3a>9}3 zXNp7qx#b4hJ0)jBFtk2#sylr0MZ2#CiuVG6pN1EQ4OxrqOJgws6JAOIFE!KNOG?t; zmZPk3^jjrv=H4?;Z79KOiuNvaz*7`35iIn5jl<}ZCL;bZ_=n)7{*M%64eJSyx$aiK zDErK~aOv1hi@mo&=vZ$#mZso%Y>rAE6 zNZu94^V8-pE)U1-s87GxG9~VZy6weA+J_wa5|h|YKW<$4@aFgs|CS$ny|LeP*V;h} z-Gc{roI?&0+DFKHY#dsGyrxJTEv;oxyLKf%Ph6xL%7}WO{UY*m(y)sspyUxqcM(@2 zsHP&AE|{lrqhvmNt>2u%9G5_RE>>Nc;E?Bp-+R|MCl>Cb z@?ytxKDMs>wF=y)Nwf|APIg(-O$(}*t>_z?w^+})|3<~B1k_x)wR<>S#kyM-ICE$- zBNv?@JmhX{()HjX+3cwm5ouOl^*qM&XaQm2-)HU>GWAlPm=^VxGb6?R!Y?;OJq*ur zyG{;-XpOxjjZo8vbq6=!zc4`SvG(KFw&^q*xP172F?gHi($$sDA?(OrHmk!X*Sf`= z@_6voqab@AiQ53KGST2;A16+|WdT&>c`;;yEHWwv=9ov*<}T{YKZ&W1sq2pMf-ps*Y%yF3fg&>mFjqZrm{bHz(Fw`C z_TkDwEe+<+$_>Z3)7c%23Hrx>&2Q7Y)~9Oo{}n&0GFNXX3k#HnXT;(5ZfA7M%RGPe zh$^y7%QN@vz=LDeV)ZLpN6(UX*cNT*csY83z)6*bt;}%(N8RaSAaCx}>ugUQ{K_Vy z3BLlXq9u*pP*FmXIdDdOXLtR~O(?1sMI&`>TSNfK@#PQp*i|~sr&nX|v zYv03{-adWh7NI?{St(ibx$ZdRHgCmHJdT+gN}~8&PM&2L;_-iLqy%dO zt?vKME-|rML!}8W3F6F`@xt@D(&qZ%`!D`PiDn7P*PFG|Pmfy~%*F09oJ`${AoSAI z)Epcdl2cV3v+Xe~mV5Q85nYRA$uMKbXZ<(F$<)}GtYAvHZ?I2PBi8?rD`0(-H649S z_-l6B)iXE8+Pa(Q>y&a+8jUp;7Xg7`q8*9i-r-*9tyBG+Bjx1lXDF}G_Wl`VorgxoR`s zeH76gr?l}avexr>GY&1OOiQ++kG3x~Qou_&?oVk7eO*4^!Q2$jdg<)I-%bl}w(;%y zY3f|Qh#h07kBW$HW6f8+R~s2gcX&f`79DZw?84Ff1`S-dL%-ciq`b9jMQq5V^1B;_&G+sE_f4r z-nRJ@D0MJ_2A`NtoCfHL&)HNym)_T2`w|z|6^8oU2S!buAZoFY5PT8#qU-c?tpEPu z!#%1o!70Y|AkSmmeHSO4d`F?5;c*v|`i^~OzZ-5)xo_hXqF=ZCT)3OupYL+RfR|uH z)Y1QK_iX$wb;MXTuHwQM4KdF@n(s+A@Sdt?VBKkc`-jmO;-9Auo)kYCOv0C0|(7dDc)7iEqoy zdeY@*H&uH^9TR*bdTmvC&08UH$iU-R5<^Xs{`PGp-LVc1@CYNle*G0p7J8CBv6fa= z76G1GUmv#5e_iOc+_IUEd~dTijWfp|sXk`^`vGx@^Fd$Llyj-;&glBgl^L-q?vx(% zt!Fd0I2HB?l;GWIFI#+Ce?xb7?ym_u{0eUBPkM^(c%iOcyM|2H28Ak{`(L}P>}+F8 z%MI0Q0^h2zBwmQagaEtEdyYJw-5(aL^FDr@*ZG=F#gB!ZN2_(6=GOB)^0dy~(kk#j z{^p;GHPh&eZ){?s3({;2n3!5qBLMTvYd!Fv`uWHR7nXCf4IR`4Q$;q6;k|z3Bij0z zl$uyhC{~Erz^XFb`-@U!U_by$$4Q`RO>HVSB{kI-+t4QmdG5X_Dk`df*tcBn zyNn6~qh*zF%p~{`&~pDjzrO$f^=qmn_JO@-eV~M-WN=3~yiL1~=J{mVcK1(GRK4Hu zuo4Qq9bV?aXNsO_ff0LiYio?AAA{Y)nf&i-4z|dA>Xej61oNi#T9T6ZFw!?p$bG!% zfS!`^_n)s{J*fHMM@k(vId^fQAT=?(0Zd1KU94)$6(@$O$BFG0b8-1g=s6V?b+a~; z;PT$yPua)BRPaL%`pU6Gy-HL^)jctV%JA{cjie|T2_pJH48!W?C4786FbDSW^^Mk) z_$jYVOt`m$yasux?Bi(-wF~f6Z|goH#~_7??1eY6h2h8LU^&$P@hc_$aK3gga06k= zczQ-gzz3nf?W>>O^@FI9fr;r3Gc#cmE0E#*$LO$A!IAO|HH^IW z1tz@NIF_qg>I!btLBNj$!B9K)OU85wn9_5(6?#Or{%2_B&Uj6E0F>0+l>zEMNa zqyxs?4^#>oR$sl-l9#^b=@~QQf}f++j`@M0 zEa8C(={PmtMJ=c!JD*8O5w5MRk>0owt0^tZ`0$|-jI{UI7_*tD0?T`Fs04SiHeh=G z8-_uLP31p^tuh5e+N>)zz=r=gUPfAa3AkVMigY&f6;l>4Xz)9XCy5epE*e|BkP3q& zBDUs1cKmbkpceL*jU_S&jgrl$O>*& zFu(7mon6V;A~qqcuFA|Got?aHhXqTUlcMm4gCirhN81J-r+>7I?N(la*I+7%6IQ&C zdrW$QOAw4q_X1WosL&H4%P8y(t3D z(lKmY`zVD!`}+C4Qh||6 z=H|V;|J^(w6n6o>M57=Qf!?_U=Xqqlc;aTFHKyn^KqY`McVOHWCd5`Tk519c0AiU$N- z{G5%?5*{A@B_iSz=h-xsCU7@HHz1k9yZz?28Ir}4MF?=ja%LSFY)XD2xtG} zS287s4YZW1DjAr&-Sqnos{OO|kxeIon%#a$!2@WEp@g+hCkSpq{=^xH&{ zK_>*QtSrX*9T)@Lz)J8r>q=q5{=Neep1`@Je%n-FZ&3bZJF>{0UAM28gAHH3KUPV@8RKFy}G7vN9Tm zOTk}OB}~Y8z^E>>(So4C zq~X-GG(Thpgg>=Q0GzFndTpT?n=pi!9W>}bAnt&-fO!!F)2Hi>m3Av&@k~sE=LNzl z*isK`HZdO`kM+WoqW5Eq0ncgDrQ>zMG8EC)BLj&hEUH65yv+s^v$-tM1P$;#kLH3O zA_M)#V1BTAnc3I8yii;kx1S5Y{fRX(uU}r7hp`6t9zOg4;d}>73}ZAy0}grYevz<5 z2E$yC!+1x&c;NY7L6uL046NvbD~aG5GCUa%AD`D{_f4HE}RrXTRFcO!43Q&xG=?>%d5elil>koN#TDyjC-) zqobpRCvUcC(w%9 rbpD^Sf&a06{vUFM{}2B+>Ks=JMRXMQmEsW!{(JFES~O2Y=iUDWsQF_8 literal 0 HcmV?d00001 diff --git a/lab6/images/benchmark_logy.png b/lab6/images/benchmark_logy.png new file mode 100644 index 0000000000000000000000000000000000000000..05f1fc381fcc2f36f850cef1e40f2d488ff430bf GIT binary patch literal 40088 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

f-QqVJOxyS z2&cm~U??ua7AS{_i-vBgB<-a;?*SxU__!$Lb=MG}*7a(xg{sHmSe zwU~8`7rZzw$0fS50m45%YxDaS5BYDQv6oMZOJ(wx{kBfY?aWOEr4tEf4m#UXa9R7r ze&xzViUaV5@&%7Zc&vLP>qTzIt);T#hG0*jFCRzyZ28L)juB9|aXo--tLjMR6hkIH6 zg4)KUN4qEpn1P39M`z#sJc1gYW?PeP5lz32AhoZc08Zd@1*H}Wnha}U_hDVA@^HrN zS@eyX?Z-Z0J6K?SaVHiR%f0UYWoTzuQHR(g+fAI*y2lk|K~bBcKQTMxXbb#JVb&t= zFL>Yd1phUfqT9a{WCtHfYF*va)$e@s%hL6T^zTRzx!;jn^6A4wKW#Os4NOUb^Ul3n zyQ|kvmr6dkZKG1=RONW=-&IB^JkeK%L8R52=(c2kKUwYgU(bK;m+sQm{akD*?Z3$5 zIzUHF#`6IAepfDC#y1pJ(Rw*oXi~>k_Sw<)=vLK7HZgAOS_!&_G4YKPpAAj<#G{S; zw67sPq+-{sXC7P1grQQ(t5j>ZoJ6sfzqJ}jMeL5&9P)P-pEbzXecE?!Tq)Y?WA%*iw=YS^Agn9e6Ri`5OxId?Wh7x7Cy~j3j zxP)JRs`R1K!4B0|Y(6`~%Gm0=NC?>jo&qoazQb)d1s{2|y{~ zSN~xI{l7e{8aL*9tPN%ABs1gJH}gQES#vSMgO+0n}IUyN$E>M)R<9c zi=7|evrof2==C5~wPzG(^&1Y^?~0yNi4MbdjIyhQ_o^D-z-{hzj`Ld+C8Nb?VHETcANTc z(ZAk5MW?m;jWeHi@8;Nk&J8rGGtUn{Az14O$Rl@d#<0muZ^biv$#bTyAOVc6NeH?oRJGNEj|mJR_n@`89EW98*5L1L3O zILc2i((Jvlb$Ddb{;cGArharI15?;(*4F73~hV8-ey z1I90Sa1Bz3=NoEm$^+yr19{}1ZxHl$ZA_ne23oB)&=RebZNI%O9Pe{)Z9;rSsQt>7 zr|Idhrb?*ZpBBF<=c=tYEJ&dZeJFBvZgxG%^~s;~ur=?lHUCC&i#00T3~k|H@-Vco zxX8^nfZ7e>yiZZ@nC+ILPA2r7zpp7GK^s7X6|JowzJI@=RhJYQO26arW!xjM>mhnb zNbW14D^J^|VWW77zIZ>DG~pliEDckY5{nuiv!yGw7PpJw38q@Wd6QUq`S{$aTa#CT zu(o5mtz6EZDpK$EK&Ib(HweTovQaosTZ)_~byQF$!8<-=*4(@Be$H(8P!G4_=!ymZ zgz3a(Up_u=E1&Ii2-t2&uOGHzsG2l{N;k~@TQ--#ahcOZ3;&CvI!&WY*V6ZJ+> zRXP0+^m4ow^})w?I1jH|A6G}C{*1|ArL18eZ+Ny6)};`KENX__rfTQ(bWfID-ssW% zJI7d5oa6&;Ts+}^R{OPKnsCn(4BtB#D{LKIw#e8OTk+!F#WHOvO8>{Xkr2B%oVC1Z ziFzQSD;Sga>?L#+*SWktxz8yM0s#V_d4!0ab0AXO$t%~dZJF!(0Rn7}lMuT-{E5N> zzeycUS1#EUi0PL%?4P{6wBe!z(Mz$pMVEuGQg#2(_YIU;(&L!1a9(B5tfRGjNhWw{ zq-yrA2NDtPJ)-;Icr}9~Lu$CB__%DmS>=h(7i;EY&Z@7`oUc%|$Lz~T@@{L41W`=> zuLoNm+BKj@J$-jHU&8q^;yWXsj^lUAuW{jI*S zzjFJ-CXU~>)sO{3d3eBPgo?ic3e#YGbLaVwAZp=^9>W(Ppy?g6+^@EE3dx6|<+Qfb z0E>*xQ}xp0H%gxy7A+?C_AJVX4_VsxHIWR7EH%1SyWZr}y#Wf{|0t`raby&;bj{Gu zF?`Z%HM?UR!xPIuJ&^_ah7 z>Lhzq&^pK0K5Ul%i}DZY0kF>qVu~RbEGk;sIwo&$u^nZ-MB$9R;!U?2jq@r@I9We% z|GBU3w#JMLqvRhq?^Ge{&jt&q_`aL!xv95!FWQAO*~jxtFDt6Y+|KDPZ1et`S>=9kX>g_wI23c8J4jA;j&VJCTj~>%wgx8)4b`nNk5C`+OqbfhxRl<`!R+ z@bLm;5aY1xYh;B?eQl+cz=9cZ)YdldpTfd;M+tbV+EEuBHCzG9+f4BY2S7XKn)f;H z@8M!s_bTQDY(^b2OM9=`W)qwp;oV@PvQZDgIBMxalfln)5I&p0W4TtXQU-4;UX#=h z;Gj_&-H2ybT;P>G>XK|eSelV*-2@$b$mL4m#R-}Pk=Y3h?d0S*#|L00gSj=4E8fuQ zq-4tnP&|ZiwUThnTQA`@d;G=xS^|lm$JdCgtU~X8^}aVKd3dI1CmmQikUxqke2u&| zwdp6vgz+OF?+OaiRgHQcHm~S*_O_|CwQT@EIJq^7-s-6uzQrj~)>84Ir-ld~7Efq$ zA3LqQNx3U%14Hr^&AlJYBInB$n3}*pr`8#w%x>o||za{@f3mjkr$`12)P08Zgzo0AxYp{wN zQ1zeo##?R*zm^##pd0_L#wGjO1o;wCkZ%w^VI)zeq?E$yuH(PqE-LaGW(>uPK<4*nKS}*-ct$ovm&C6u}v7zj50cnP3H}c7=B+s})1J6(H z!=+J3pBxJPd;!t98g+e3SWq>6Yf5!X!|H%*Ft zXKHq#P~abF#1(*44=g`^{F|$ZFY(i3FSU=-qjY$pW~je4nO@$2c;liTP3FJl5)Du6 zQKxE~GW)G{pbxC!Un~xFq_6g3Sw7H|J5A3G@RQ9Weu^9hxuD$7nd&~s32Vs!^?=Da z#c_5mT&Z#O%%_2WI%?6*55Wgn1 z4mcAfxX14OAB)WXCftr!GgXe0nP2y{S!}T@sd~C8;wKTuh`di0K*i+VCa)v3w#!GITtT!*I z6e5~DXIw;=)-%7zjkyOTfvkj#qz5J18w`E)Yrjeixxe4be=rV~0CBK9gmU^HgGxL2 ztAMcs&Bopy8wbbN($!oWgu?K&)(B53+2efhgZaj@hd0O1Q8UjweaVr8zLPr0&qKEh zyniUj$zL~IPD`w$P*c0f&dvg=0^X~mRaFA1yP)8#2!f`CF&tR+YnyGhkhRP&VNzh# zB343xu8|IB5(OUsdk2BMWXqnS`QqQQm?c*NAy`d~3g$;($%X}Z zR>ao>X6>?uhABCc7EFQI)6I1kc}($2aV{DUW2Gd&^@ZYe@Lhp{Ui|T~Qm)({%4)3o zBbey*6qzVh*VKFjI|GZsJO>BOj%k~E6WMPL*KeF}9~8%{Ih$C_{I{M+9OiM>|G&rs z?07)OKFy>n>F@~r#$JJxKhsF-^7u*ZuaNI6wr9R3=WrALJcq?StQ2dntOD7L4*> z2tHtP-hm*SAbDJFbus*>(#Guk>L+N{=K$_Bh-69gzF-~mZOiQ?=Vui;l9qi4Z+(x$bY=^rRN09315Qw)QC+ZF$&=&xPu-*^)AK& zY-qfhL|p}T_8CQ8-$$^>@VVB48N)4$fqq;Fmf91Xoll1Fzv|RpAVvk^~g`)^(FOtFnUXnEPL z;nRe}n2U3`;CMedXv7507~kz~9QSRWCA~plzvtA_@n@4c84LraVO)ibtXjId!c$T% zGr7)Rd8&qMxG~kVy*6GqW{YsVKLs*9?@5*Vs$CL{xYXPT_DQ{AIa01-9h;QY307g? zkBAKK4^5th0^Btj78O_$fz#q_D_gQXhMAGEqwaKv3#QSbV0;9_rfP7YGqDeMaNBvR z_7dJ0yc3=mXo3$R9(c-RoSN8zL&a*v9Mg$DiiAnzCm6QiU${`;*7g?Oc>|o=l2wQv z^!{&W3t_OeP#l(&lx%Hp$EkmDX9HZ9?f`H7JSkKA@xhjoAoe~O0)GDXP5Dd+_z^i{ zlD@zB>Wf$=W2sQ@(MQo!XC8Rxv9IinkMC|E6f70vT=VUm@9EKDFt$ISx&9>4u0Ji? zrp13lkyoBx*u)NQ0|Cg20JC1J)?E;mNWa|sjwI1|QMcW1V4+!af=oHW7=_Cx1L(`a z2B>1Sa;Y31m!Rjd07R6z5xcJb;nNjsuW2yNc?lSS?Zy7LV3QU8RL#uRYP2#M@fiYG zeUaUqB4XKx3`~ASvnNiuf#)A$E>U-S$Zj{+Ia4}pWdvR$9hHN~UC&5z4W==0kAS(q z*x@|Oc_La=Jsz{W1k9Y@(r^u|;Ti%j7JOJXSIWi<;jNiq&LrOB6DDT|_}2|A%Nn?9y|Hk&bz5-}5hx zg6R1NDt=Q*Z&1j3fOV-A%jIC$o^^+~cRcxQ|NQ3TCGk6A-~P6<^MH)T`hONs;7GYV zQW1rGW)l12$Hjei_Yw%Zyuu_!NB2*Nhmc%Bi!vOkdFdQ)RJ}jjr4P=Hg_E%} zEcM0*rH!DndrNJJcAbKXbIf2?1_oBHwoCAZ*TK6|MMdRxzxfPAo)-WkQ4rb#8?2qd zdu?g`^1&lTCZymvm!x_GwoF|M7`9|d6-uysjT0@{#8~8isjg0dn}hhxfB{wJ-Xp$Q za3TB4e727VKgC2Ue#|ZGxNJI5BE`0fW&eMKDU!)&{!^v^&zmX>Ukeg7|9qN(p_GXk zhj&)|?cZs;iR~T*wLO4G8)mpS)!gDtulRV;Q%@kmjO+Br>a>)=Q|=qxWmQ}jR)Tob zvKEA1XZ8E)Z_2KyW+*m2FF@o|rer0rE z?^0p8desL)eSy`e5YW9Wzz(eqd@vgjsPmf8J*&zER;2i<`fT#MKO!## zv$0wrfw^9k?CkrmtH=aiP#^@8*k2L%5NYOlFRI)m5x#L=nMfM#HiC00v5*zMv=;2v z=Z59X!eNwmxJSZ4TSfaM4p#lun>XL+SXKCg_2Nc5Z&zP@s9UcBe-C(Xdb`T%fb*h( z#|{-lEf#L>Z>`BHTXS6|KZ3|b4(HPlaS-q-+E^~?+Jqoi(jI!x5Hv&ntT+ND%0DB6 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 F{{?^&!+ih% literal 0 HcmV?d00001 diff --git a/lab6/main.ipynb b/lab6/main.ipynb new file mode 100644 index 0000000..00a47bd --- /dev/null +++ b/lab6/main.ipynb @@ -0,0 +1,94 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A. TISS-Feedback (optional, ~15min)\n", + "\n", + "Diese Aufgabe ist optional; wir bitten Sie sich dennoch explizit Zeit zu nehmen, und uns Rückmeldungen zu geben.\n", + "\n", + "Bitte nutzen Sie auch die Freitext Felder, diese sind für uns besonders wertvoll.\n", + "\n", + "Hier ein paar Anregungen für Ihre Rückmeldungen via Freitext:\n", + "\n", + "- Bitte wenn es geht immer kurz Ihre Vorerfahrung erwähnen, z.B. mit \"Vorerfahrung: viel\" oder \"Vorerfahrung: wenig\"\n", + "- Bitte beziehen Sie gerne auch die vorangehende LV (Python 360.049) in Ihre Überlegungen mit ein.\n", + "- Bitte beziehen Sie gerne auch explizit auf einzelne Aspekte des diessemestrigen Modus ein (Hörsaal/Hausübungen/Labor Abgaben/Labor Praxisteil)\n", + "\n", + "TISS-Fragebogen: https://tiss.tuwien.ac.at/survey/surveyForm.xhtml?dswid=1580&dsrid=261&courseNumber=360050&semesterCode=2024S\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## B. Laufzeitmessung für eine Ausgleichungsrechnung (*polynomial fitting*)\n", + "\n", + "[taskB.py](taskB.py): gegebener Python-Quellcode zur Ausgleichungsrechnung mit Polynomen (ident zu [360.049/homework8/task3](https://sgit.iue.tuwien.ac.at/360049/homework8/src/branch/main/main.ipynb#user-content-Aufgabe-3:-Ausgleichungsrechnung-mit-Polynomen-(1-Punkt))).\n", + "\n", + "[taskB.cpp](taskB.cpp): gegebener C++-Quellcode mit Funktionalität analog zu [taskB.py](taskB.py).\n", + "\n", + "Beide Quellcodes berechnen die Koeffizienten eines Ausgleichploynoms für einen generisch erzeugten Datensatz von Funktionswertepaaren (+ Rauschen) im Intervall $[0,5]$ für die Funktion: \n", + "\n", + "$f(x) = 2(1 - \\exp^{-x})$\n", + "\n", + "**Aufgabe:**\n", + "\n", + "1. Inspizieren Sie den gegebenen Programmcode.\n", + "2. Führen Sie den die Programme aus (die Anzahl an Datenpunkten ist im Quellcode mit 30000 festgelegt).\n", + "\t```shell\n", + "\tpython taskB.py \n", + "\t./taskB \n", + "\t```\n", + "3. Gestalten Sie beide gegebenen Programme um, so dass die Anzahl an Datenpunkten als Argument in der Kommandozeile übergeben werden kann, z.B. so:\n", + "\t```shell\n", + "\tpython taskB.py 30000 \n", + "\t./taskB 30000\n", + "\t```\n", + "4. Messen Sie die Laufzeit für die Ausgleichungsrechnung für mindestens 5 verschiedene Anzahlen an Datenpunkten,\n", + "\t- für die Python-Implementierung (taskB.py),\n", + "\t- für die C++-Implementierung mit Debug Einstellungen (`-O0`), und\n", + "\t- für die C++-Implementierung it Release Einstellungen\t(`-O3`).\n", + "5. Erstellen Sie einen Plot der Laufzeiten, dies könnte z.B. so aussehen:\n", + "\n", + "\t![benchmark](images/benchmark_lin.png)\n", + "\t\n", + "\t![benchmark](images/benchmark_logy.png)\t\n", + "\n", + "\n", + "**Demonstration:**\n", + "\n", + "- Welche Bibliothek kommt in der C++-Implementierung (statt numpy) zum Einsatz.\n", + "- Warum sind die errechneten Koeffizienten nicht exakt gleich (Python vs C++). \n", + "- Präsentieren Sie Ihre Messungen/Plot\n", + "- Diskutieren Sie die Laufzeitunterschiede.\n", + "- Diskutieren Sie die Abhängigkeit der Laufzeit von der Problemgrösse.\n", + "- Diskutieren Sie mögliche Ursachen für die Abhängigkeit (z.B. Speicherzugriffszeiten, Rechenoperationen)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab6/modules b/lab6/modules new file mode 160000 index 0000000..33515ac --- /dev/null +++ b/lab6/modules @@ -0,0 +1 @@ +Subproject commit 33515ac3ade8381f1336333051f85c7f63c8705c diff --git a/lab6/taskB.cpp b/lab6/taskB.cpp new file mode 100644 index 0000000..2c8e87e --- /dev/null +++ b/lab6/taskB.cpp @@ -0,0 +1,101 @@ +/// @file +/// @brief Task A +/// Debug: g++ -O0 -g -std=c++20 taskB.cpp -Ieigen -Imodules -o taskB && ./taskB +/// Release: g++ -DNDEBUG -O3 -std=c++20 taskB.cpp -Ieigen -Imodules -o taskB && ./taskB + +// http://eigen.tuxfamily.org/dox/group__QuickRefPage.html#title4 +#include // MatrixXd, VectorXd + +#include +#include +#include +#include +#include + +/// @brief Funtionality equivalent function 'poly_fit' in taskA.py +std::vector poly_fit(std::vector x_coords, std::vector y_coords, size_t order) { + + assert(x_coords.size() == y_coords.size()); + + using Eigen::MatrixXd; + using Eigen::VectorXd; + + size_t m = y_coords.size(); + size_t n = order + 1; + + auto A = MatrixXd(m, n); + auto b = VectorXd(m); + + for (size_t i = 0; i != m; ++i) { + auto row = VectorXd(n); + for (size_t j = 0; j != n; ++j) { + row(j) = std::pow(x_coords[i], j); + } + A.row(i) = row; + b(i) = y_coords[i]; + } + + using std::chrono::duration; + using std::chrono::duration_cast; + using std::chrono::high_resolution_clock; + using std::chrono::milliseconds; + + auto t1 = high_resolution_clock::now(); + MatrixXd x = (A.transpose() * A).ldlt().solve(A.transpose() * b); + auto t2 = high_resolution_clock::now(); + + duration ms_double = t2 - t1; + std::cout << "N =" << x_coords.size() << " runtime: " << ms_double.count() << "ms\n"; + std::vector res(&x(0), x.data() + x.cols() * x.rows()); + return res; +} + +int main(int argc, char* argv[]) { + + /// @todo Change implementation s.t. rhe value for N can be supplied via a command line argument + size_t N = 30000; + + { + std::random_device rd; + std::mt19937 gen(1); + std::normal_distribution dis(0, 0.2); + + auto x = std::vector(N, 0); + auto y = std::vector(N, 0); + for (size_t i = 0; i != N; ++i) { + x[i] = 0 + i * (5.0 - 0) / (N - 1); + //y = 2*(1 - exp(-x)) + y[i] = 2.0 * (1.0 - std::exp(-x[i])); + } + + { + auto coeffs = poly_fit(x, y, 3); + std::cout << "[ "; + for (const auto& coeff : coeffs) { + std::cout << coeff << " "; + } + std::cout << "]" << std::endl; + } + for (size_t i = 0; i != N; ++i) { + y[i] = y[i] + dis(gen); + } + + { + auto coeffs = poly_fit(x, y, 3); + std::cout << "[ "; + for (const auto& coeff : coeffs) { + std::cout << coeff << " "; + } + std::cout << "]" << std::endl; + } + { + auto coeffs = poly_fit(x, y, 4); + std::cout << "[ "; + for (const auto& coeff : coeffs) { + std::cout << coeff << " "; + } + std::cout << "]" << std::endl; + } + } + return 0; +} diff --git a/lab6/taskB.py b/lab6/taskB.py new file mode 100644 index 0000000..1149801 --- /dev/null +++ b/lab6/taskB.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +import numpy as np +import matplotlib.pyplot as plt +import time + +def poly_fit(x, y, order): + """ + Fits the coefficients of a polynomial function to + a set of two-dimensional data points using the least-squares method. + + Note: uses the function 'numpy.linalg.lstsq' which returns a tuple, where the first item is the solution vector. + + Parameters + ---------- + x : list + x-coordinates of the data points + y : list + y-coordinates of the data points + order: int + Order of the polynomial to fit the data, see eq. (6) in 'main.ipynb' for the polynomial form: + https://sgit.iue.tuwien.ac.at/360049/homework8/src/branch/main/main.ipynb#user-content-Aufgabe-3:-Ausgleichungsrechnung-mit-Polynomen-(1-Punkt) + + Returns + ------- + list + Coefficients of the polynomial + """ + A = np.array([[xi**n for n in range(order+1)] for xi in x]) + b = np.array(y) + start_time = time.perf_counter() + # coeff,res,rank,s = np.linalg.lstsq(A, b, rcond=None) + coeff = np.linalg.solve(A.T@A, A.T@b) + end_time = time.perf_counter() + execution_time_ms = float((end_time - start_time) * 1000) + print(f"N={len(x)} runtime: {execution_time_ms}ms") + return coeff + + +def plot_plot(x, y, func, filename): + + plt.figure() + + plt.plot(x, y, marker="o", linestyle="", label="Data") + + x_samples = np.linspace(min(x),max(x),100) + yp = func(x_samples) + + plt.plot(x_samples, yp, label="Fitting") + + plt.xlabel("x") + plt.ylabel("y") + plt.legend() + plt.savefig(filename) + + +if __name__ == "__main__": + + + + N = 30000 + + + # w/o noise, , fit n=3 + x = np.linspace(0, 5, N) + y = 2*(1 - np.exp(-x)) # y = 2*(1 - exp(-x)) + n = 3 + coeff = poly_fit(x, y, n) + print(coeff) + + # note: plotting disabled + # func = lambda x : sum([coeff[i]*x**i for i in range(0, n+1)]) + # plot_plot(x, y, func, "taskA_wo_noise_n3.png") + + # exponential w/ noise, fit n=3 + mean = 0.0 + sigma = 0.2 + y = y + np.random.normal(mean, sigma, len(y)) + n = 3 + coeff = poly_fit(x, y, n) + print(coeff) + + # note: plotting disabled + # func = lambda x : sum([coeff[i]*x**i for i in range(0, n+1)]) + # plot_plot(x, y, func, "taskA_w_noise_n3.png") + + # exponential w/ noise, fit n=4 + n = 4 + coeff = poly_fit(x, y, n) + print(coeff) + + # note: plotting disabled + # func = lambda x : sum([coeff[i]*x**i for i in range(0, n+1)]) + # plot_plot(x, y, func, "taskA_w_noise_n4.png") +