diff --git a/.gitignore b/.gitignore index 9d3546d..04fcb10 100644 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,20 @@ exercises/2.1-class-types/test exercises/2.2-complex/test exercises/3-containers/part1/test exercises/3-containers/part2/test +exercises/4-pointers/pointers exercises/5-templates/part1/sum exercises/5-templates/part2/test +exercises/6.1-my-array/part*/my_array +exercises/6.2-special-pointers/part1/unique +exercises/6.2-special-pointers/part2/shared exercises/8-algorithm/ex exercises/9-eigen/explicit exercises/9-eigen/implicit exercises/9-eigen/sparse exercises/9-eigen/*.txt exercises/9-eigen/*.gif -exercises/10-threads/area \ No newline at end of file +exercises/10-threads/area + +lectures/1-cpp-intro/auto/auto +lectures/1-cpp-intro/hello/hello +lectures/1-cpp-intro/sum/sum diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..2b27819 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Rupert Nash +Joseph Lee \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2f244ac --- /dev/null +++ b/LICENSE @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md index 631e008..3b2be73 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Modern C++ for Computational Scientists -Repository view: +[Repository view](https://github.com/EPCCed/archer2-cpp/) -Pages view: +[Pages view](https://epcced.github.io/archer2-cpp/) Since the 2011 revision to the C++ language and standard library, the ways it is now being used are quite different. Used well, these diff --git a/exercises/10-threads/area.cpp b/exercises/10-threads/area.cpp index 3249dc7..898dd9a 100644 --- a/exercises/10-threads/area.cpp +++ b/exercises/10-threads/area.cpp @@ -1,10 +1,6 @@ #include #include #include -#include -#include -#include -#include using complex = std::complex; @@ -20,66 +16,27 @@ bool in_mandelbrot(const complex& c) { return true; } -void area( - int start, - int stop, - int npoints, - double scale_real, - double scale_imag, - int& num_inside, - std::mutex& mutex -) { - const auto eps = 1.0e-7; - const auto shift = complex{-2.0 + eps, 0.0 + eps}; - - for (int i = start; i < stop; ++i) { - for (int j = 0; j < npoints; ++j) { - const auto c = shift + complex{ - (scale_real * i) / npoints, - (scale_imag * j) / npoints - }; - if (in_mandelbrot(c)) { - mutex.lock(); - num_inside++; - mutex.unlock(); - } - } - } -} - int main() { const auto NPOINTS = 2000; + const auto scale_real = 2.5; const auto scale_imag = 1.125; - + const auto eps = 1.0e-7; + const auto shift = complex{-2.0 + eps, 0.0 + eps}; using clock = std::chrono::high_resolution_clock; auto start = clock::now(); - std::mutex mutex; + // Outer loops run over npoints, initialise z=c + // Inner loop has the iteration z=z*z+c, and threshold test int num_inside = 0; - - int n_threads = 16; - std::vector threads; - - int points_per_thread = NPOINTS / n_threads; - int i = 0; - int j = 0; - - for (int id = 0; id < n_threads; ++id) { - i = id * points_per_thread; - j = i + points_per_thread; - threads.push_back( - std::thread( - area, i, j, NPOINTS, scale_real, scale_imag, std::ref(num_inside), std::ref(mutex) - ) - ); - } - - // Wait for all threads to finish - for (auto&& thread : threads) { - thread.join(); + for (int i = 0; i < NPOINTS; ++i) { + for (int j = 0; j < NPOINTS; ++j) { + const auto c = shift + complex{(scale_real * i) / NPOINTS, + (scale_imag * j) / NPOINTS}; + if (in_mandelbrot(c)) + num_inside++; + } } - auto finish = clock::now(); // Calculate area and error and output the results diff --git a/exercises/10-threads/instructions.md b/exercises/10-threads/instructions.md index ff36f3d..0f8d185 100644 --- a/exercises/10-threads/instructions.md +++ b/exercises/10-threads/instructions.md @@ -5,7 +5,7 @@ Source for this can be obtained from Github. Get a new copy with: ``` -git clone https://github.com/EPCCed/archer2-CPP-2021-07-20 +git clone https://github.com/EPCCed/archer2-cpp ``` or update your existing one with @@ -17,11 +17,9 @@ git pull then you can ``` -cd archer2-CPP-2021-07-20/exercises/threads +cd archer2-cpp/exercises/threads ``` - - `area.cpp` contains a C++ version of the Mandelbrot example which you used in Threaded Programming. Parallelise the outer loop of the main computation using C++ diff --git a/exercises/2.1-class-types/complex.cpp b/exercises/2.1-class-types/complex.cpp index c6366d2..f640699 100644 --- a/exercises/2.1-class-types/complex.cpp +++ b/exercises/2.1-class-types/complex.cpp @@ -8,25 +8,21 @@ Complex::Complex(double real, double imag) : re(real), im(imag) { } double Complex::real() { - return re; + // Return real component } -double Complex::imag() { - return im; -} +/* Add definition of a member function to access the imaginary component */ Complex Complex::conj() { - return Complex{re, -im}; + // Return complex conjugate } -double Complex::norm() { - return std::sqrt(re*re + im*im); -} +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ -Complex Complex::add(Complex other_complex) { - return Complex{re + other_complex.re, im + other_complex.im}; -} +/* Add definition of 'add' member function */ bool Complex::equals(Complex other_complex) { - return re == other_complex.re && im == other_complex.im; -} + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/2.1-class-types/complex.hpp b/exercises/2.1-class-types/complex.hpp index 25d7588..c7fda7b 100644 --- a/exercises/2.1-class-types/complex.hpp +++ b/exercises/2.1-class-types/complex.hpp @@ -4,32 +4,34 @@ // Simple complex number class class Complex { public: - // Default value is zero - Complex() = default; - // Construct purely real complex - Complex(double real); - // Construct from real and imaginary parts - Complex(double real, double imag); + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ // Access components double real(); - double imag(); + /* Add declaration to access the imaginary component */ // Compute the complex conjugate Complex conj(); - // Compute the magnitude - double norm(); + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ - // Add two complex numbers - Complex add(Complex other_complex); + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ // Check if two complex numbers are equal bool equals(Complex other_complex); -private: - double re = 0.0; - double im = 0.0; + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ }; -#endif +#endif \ No newline at end of file diff --git a/exercises/2.1-class-types/test.cpp b/exercises/2.1-class-types/test.cpp index d4e3e1f..fa707a9 100644 --- a/exercises/2.1-class-types/test.cpp +++ b/exercises/2.1-class-types/test.cpp @@ -93,4 +93,4 @@ TEST_CASE("Complex number can be added") { CheckZminusZeq0(-1); CheckZminusZeq0(Complex{1, 2}); CheckZminusZeq0(Complex{-42, 1e-3}); -} +} \ No newline at end of file diff --git a/exercises/2.2-complex/README.md b/exercises/2.2-complex/README.md index 9fe8a98..d3a7289 100644 --- a/exercises/2.2-complex/README.md +++ b/exercises/2.2-complex/README.md @@ -11,7 +11,12 @@ The files `complex.hpp` and `complex.cpp` contain a partially working comple You can compile and run with: ``` -$ make && ./testc++ --std=c++14 -I../include -c -o complex.o complex.cppc++ --std=c++14 -I../include -c -o test.o test.cppc++ complex.o test.o -o test===============================================================================All tests passed (34 assertions in 6 test cases) +$ make && ./test +g++ --std=c++14 -I../include -c -o complex.o complex.cpp +g++ --std=c++14 -I../include -c -o test.o test.cpp +g++ complex.o test.o -o test +=============================================================================== +All tests passed (34 assertions in 6 test cases) ``` But to get to this point you need to complete the code and fix a few bugs! diff --git a/exercises/2.2-complex/complex.cpp b/exercises/2.2-complex/complex.cpp index b51c993..a27600a 100644 --- a/exercises/2.2-complex/complex.cpp +++ b/exercises/2.2-complex/complex.cpp @@ -1,13 +1,9 @@ #include "complex.hpp" #include -Complex::Complex(double real) : re(real) { -} -Complex::Complex(double real, double imag) : re(real), im(imag) { -} -double Complex::real() const { +double const& Complex::real() { return re; } @@ -28,7 +24,7 @@ double Complex::norm2() const { } bool operator==(Complex const& a, Complex const& b) { - return (a.re == b.re) && (a.im == b.im); + return (a.re == b.re) && (a.im == b.re); } bool operator!=(Complex const& a, Complex const& b) { return !(a == b); @@ -38,16 +34,9 @@ Complex operator+(Complex const& a, Complex const& b) { return Complex{a.re + b.re, a.im + b.im}; } -Complex operator-(Complex const& a, Complex const& b) { - return Complex{a.re - b.re, a.im - b.im}; -} Complex operator*(Complex const& a, Complex const& b) { // (a + ib)*(c + id) == (a*c - b*d) + i(b*c + a*d) - return Complex{ - a.re*b.re - a.im*b.im, - a.re*b.im + a.im*b.re - }; } Complex operator-(Complex const& a) { diff --git a/exercises/2.2-complex/complex.hpp b/exercises/2.2-complex/complex.hpp index d8593ab..c623b28 100644 --- a/exercises/2.2-complex/complex.hpp +++ b/exercises/2.2-complex/complex.hpp @@ -7,16 +7,14 @@ class Complex { // Default value is zero Complex() = default; // Construct purely real complex - Complex(double real); // Construct from real and imaginary parts - Complex(double real, double imag); // Access components double real() const; double imag() const; // Compute the complex conjugate - Complex conj() const; + Complex conj(); // Compute the magnitude and squared magnitude double norm() const; @@ -32,8 +30,6 @@ class Complex { friend Complex operator*(Complex const& a, Complex const& b); friend Complex operator/(Complex const& a, Complex const& b); // Question: how would you declare multiplication and division by a real number? - friend Complex operator*(Complex const& a, double const& b); - friend Complex operator*(double const& a, Complex const& b); // Unary negation friend Complex operator-(Complex const& a); diff --git a/exercises/3-containers/part1/vector_ex.cpp b/exercises/3-containers/part1/vector_ex.cpp index f7175db..00c2828 100644 --- a/exercises/3-containers/part1/vector_ex.cpp +++ b/exercises/3-containers/part1/vector_ex.cpp @@ -4,23 +4,5 @@ // https://en.cppreference.com/w/cpp/container/vector std::vector GetEven(std::vector const& source) { - auto ans = std::vector{}; - for (auto&& elem: source) { - if (elem % 2 == 0) - ans.push_back(elem); - } - return ans; } -void PrintVectorOfInt(std::ostream& output, std::vector const& data) { - output << "[ "; - bool first = true; - for (auto const& x: data) { - if (!first) { - output << ", "; - } - first = false; - output << x; - } - output << "]"; -} diff --git a/exercises/3-containers/part2/vector_ex.cpp b/exercises/3-containers/part2/vector_ex.cpp index f7175db..00c2828 100644 --- a/exercises/3-containers/part2/vector_ex.cpp +++ b/exercises/3-containers/part2/vector_ex.cpp @@ -4,23 +4,5 @@ // https://en.cppreference.com/w/cpp/container/vector std::vector GetEven(std::vector const& source) { - auto ans = std::vector{}; - for (auto&& elem: source) { - if (elem % 2 == 0) - ans.push_back(elem); - } - return ans; } -void PrintVectorOfInt(std::ostream& output, std::vector const& data) { - output << "[ "; - bool first = true; - for (auto const& x: data) { - if (!first) { - output << ", "; - } - first = false; - output << x; - } - output << "]"; -} diff --git a/exercises/4-pointers/Makefile b/exercises/4-pointers/Makefile new file mode 100644 index 0000000..a29826c --- /dev/null +++ b/exercises/4-pointers/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +pointers : pointers.o + $(CXX) $^ -o $@ + +run : pointers + ./pointers + +clean : + rm -rf *.o pointers \ No newline at end of file diff --git a/exercises/4-pointers/pointers.cpp b/exercises/4-pointers/pointers.cpp new file mode 100644 index 0000000..6cb8d0a --- /dev/null +++ b/exercises/4-pointers/pointers.cpp @@ -0,0 +1,61 @@ +#include +#include + + +// Print as you go +// Print the value of x +// Print the address of x +// Print the pointer to x +// Print the dereferenced pointer to x +// Print the address of the pointer to x + +int main() { + +// TODO: Create an int x and set it to a value + + +// TODO: Create a point to x called x_ptr + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing + + +std::cout << "----------" << std::endl; + +// TODO: Dereference the pointer to x to increment x's value by one + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing + + +std::cout << "----------" << std::endl; + +// TODO: Create a double array of size 4 and give it some values + + +// TODO: Create a pointers to the 0th and 1st elements in y +// Call them y0_ptr and y1_ptr + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing but for y + + +std::cout << "----------" << std::endl; + +// TODO: Increment the y0_ptr + + +std::cout << "----------" << std::endl; + +// TODO: Do all of the printing but for y + +std::cout << "----------" << std::endl; + +return 0; +} \ No newline at end of file diff --git a/exercises/5-templates/README.md b/exercises/5-templates/README.md index 7bf6d85..a4744e8 100644 --- a/exercises/5-templates/README.md +++ b/exercises/5-templates/README.md @@ -1,4 +1,4 @@ -# Containers exercise +# Templates exercise In your clone of this repository, find the `5-templates` exercise. It contains two sub-directories `part1` and `part2`. @@ -20,7 +20,8 @@ Makefile sum.cpp 13 // sum("Hello", "World!"); ``` 3. Uncomment line 13. What happens when you try to compile? -4. Change the `sum()` function to use type templating. How does this change the output? Hint: C++ will not automatically convert from a char array to `std::string` so you will need to be explicit. +3. Change the `sum()` function to use type templating. How does this change the output? Hint: C++ will not automatically convert from a char array to std::string so you will need to be explicit. + ## Part 2 diff --git a/exercises/5-templates/part1/sum.cpp b/exercises/5-templates/part1/sum.cpp index 5d56b99..15bdcfb 100644 --- a/exercises/5-templates/part1/sum.cpp +++ b/exercises/5-templates/part1/sum.cpp @@ -1,8 +1,7 @@ #include -template -T sum(const T& a, const T& b) { - T result = a + b; +int sum(const int& a, const int& b) { + int result = a + b; std::cout << a << " + " << b << " = " << result << std::endl; return result; @@ -15,5 +14,5 @@ int main() { // 3.2 + 5.1 = 8.3 sum(3.2, 5.1); - sum("Hello", "World!"); + // sum("Hello", "World!"); } \ No newline at end of file diff --git a/exercises/5-templates/part2/Makefile b/exercises/5-templates/part2/Makefile index d63bfbd..ca3fc0c 100644 --- a/exercises/5-templates/part2/Makefile +++ b/exercises/5-templates/part2/Makefile @@ -1,6 +1,6 @@ CXXFLAGS = --std=c++17 -I../../include -test : test.o +test : complex.o test.o $(CXX) $^ -o $@ run : test diff --git a/exercises/5-templates/part2/complex.cpp b/exercises/5-templates/part2/complex.cpp new file mode 100644 index 0000000..5648072 --- /dev/null +++ b/exercises/5-templates/part2/complex.cpp @@ -0,0 +1,51 @@ +#include "complex.hpp" +#include + +// Hint: Templates should be defined in the .hpp file +// Will any of these functions remain here? + +Complex::Complex(double real) : re(real) {} +Complex::Complex(double real, double imag) : re(real), im(imag) {} + +double Complex::real() const { + return re; +} + +double Complex::imag() const { + return im; +} + +Complex Complex::conj() const { + return Complex{re, -im}; +} + +double Complex::norm() const { + return std::sqrt(norm2()); +} + +double Complex::norm2() const { + return re*re + im*im; +} + +bool operator==(Complex const& a, Complex const& b) { + return (a.re == b.re) && (a.im == b.im); +} +bool operator!=(Complex const& a, Complex const& b) { + return !(a == b); +} + +Complex operator+(Complex const& a, Complex const& b) { + return Complex{a.re + b.re, a.im + b.im}; +} + +Complex operator-(Complex const& a, Complex const& b) { + return Complex{a.re - b.re, a.im - b.im}; +} + +Complex operator*(Complex const& a, Complex const& b) { + return Complex{a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re}; +} + +Complex operator-(Complex const& a) { + return Complex{-a.re, -a.im}; +} diff --git a/exercises/5-templates/part2/complex.hpp b/exercises/5-templates/part2/complex.hpp index 5da1484..70a7e50 100644 --- a/exercises/5-templates/part2/complex.hpp +++ b/exercises/5-templates/part2/complex.hpp @@ -1,109 +1,52 @@ #ifndef CPPEX_COMPLEX_COMPLEX_HPP #define CPPEX_COMPLEX_COMPLEX_HPP -#include // Simple complex number class -template +/* Add template typename definition here */ class Complex { public: // Default value is zero Complex() = default; - Complex(T real); - Complex(T real, T imag); + Complex(double real); + Complex(double real, double imag); // Access components - T real() const; - T imag() const; + double real() const; + double imag() const; // Compute the complex conjugate Complex conj() const; // Compute the magnitude and squared magnitude double norm() const; - T norm2() const; + double norm2() const; // Declare comparisons - template - friend bool operator==(Complex const& a, Complex const& b); - template - friend bool operator!=(Complex const& a, Complex const& b); + friend bool operator==(Complex const& a, Complex const& b); + friend bool operator!=(Complex const& a, Complex const& b); - // Declare binary arithmetic operators - template - friend Complex> operator+(Complex const& a, Complex const& b); - template - friend Complex> operator-(Complex const& a, Complex const& b); - template - friend Complex> operator*(Complex const& a, Complex const& b); + // Declare binary arithmetic operators - Assume both complex numbers have the same template type. + friend Complex operator+(Complex const& a, Complex const& b); + friend Complex operator-(Complex const& a, Complex const& b); + friend Complex operator*(Complex const& a, Complex const& b); + + /* + Extension: What if I want to add a Complex to a Complex? + What would be returned in this case? What should the return type + be to make this flexible? + Hint: Look up std::common_type from the type_traits header. + + If you complete this extension, uncomment the final test case in + test.cpp to test your implementation. + */ // Unary negation - template - friend Complex operator-(Complex const& a); + friend Complex operator-(Complex const& a); private: - T re = 0.0; - T im = 0.0; + double re = 0.0; + double im = 0.0; }; -template -Complex::Complex(T real) : re(real) {} - -template -Complex::Complex(T real, T imag) : re(real), im(imag) {} - -template -T Complex::real() const { - return re; -} - -template -T Complex::imag() const { - return im; -} - -template -Complex Complex::conj() const { - return Complex{re, -im}; -} - -template -double Complex::norm() const { - return std::sqrt(norm2()); -} - -template -T Complex::norm2() const { - return re*re + im*im; -} - -template -bool operator==(Complex const& a, Complex const& b) { - return (a.re == b.re) && (a.im == b.im); -} - -template -bool operator!=(Complex const& a, Complex const& b) { - return !(a == b); -} - -template -Complex> operator+(Complex const& a, Complex const& b) { - return Complex>{a.re + b.re, a.im + b.im}; -} - -template -Complex> operator-(Complex const& a, Complex const& b) { - return Complex>{a.re - b.re, a.im - b.im}; -} - -template -Complex> operator*(Complex const& a, Complex const& b) { - return Complex>{a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re}; -} - -template -Complex operator-(Complex const& a) { - return Complex{-a.re, -a.im}; -} #endif diff --git a/exercises/5-templates/part2/test.cpp b/exercises/5-templates/part2/test.cpp index 265687e..27aa8a8 100644 --- a/exercises/5-templates/part2/test.cpp +++ b/exercises/5-templates/part2/test.cpp @@ -97,11 +97,6 @@ TEST_CASE("Complex numbers can be multiplied") { REQUIRE(z == -i); } -bool AreDoubleSame(double dFirstVal, double dSecondVal) -{ - return std::fabs(dFirstVal - dSecondVal) < 2 * std::numeric_limits::epsilon(); -} - TEST_CASE("Complex numbers can be templated") { const Complex z1{static_cast(3.4), static_cast(5.1)}; const Complex z2{3.4, 5.1}; @@ -109,8 +104,17 @@ TEST_CASE("Complex numbers can be templated") { REQUIRE(z1.real() == 3); REQUIRE(z1.imag() == 5); REQUIRE(z1.norm2() == 34); +} + +/* +Uncomment the code below to test the binary operators extension exercise + +TEST_CASE("Complex number binary operators can use two different template types") { + const Complex z1{3, 5}; + const Complex z2{3.4, 5.1}; REQUIRE(z1 + z2 == Complex{6.4, 10.1}); REQUIRE(std::is_same>::value); REQUIRE(std::is_same>::value); REQUIRE(std::is_same>::value); } +*/ diff --git a/exercises/6.1-my-array/part1/Makefile b/exercises/6.1-my-array/part1/Makefile new file mode 100644 index 0000000..46c8093 --- /dev/null +++ b/exercises/6.1-my-array/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +my_array : my_array.o + $(CXX) $^ -o $@ + +run : my_array + ./my_array + +clean : + rm -rf *.o my_array \ No newline at end of file diff --git a/exercises/6.1-my-array/part1/my_array.cpp b/exercises/6.1-my-array/part1/my_array.cpp new file mode 100644 index 0000000..42c172e --- /dev/null +++ b/exercises/6.1-my-array/part1/my_array.cpp @@ -0,0 +1,27 @@ +#include + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) { + std::cout << "Constructing: " << data << std::endl; + // TODO: Implement constructor + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) { + std::cout << "Constructing: " << data << std::endl; + // TODO: Implement constructor + } + + // Copy constructor + my_array(my_array const& other) { + std::cout << "Copy constructing: " << data << std::endl; + // TODO: Implement copy constructor + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + // TODO: Implement copy assignment operator + std::cout << "Copy assigning: " << data << std::endl; + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < + +class my_array { + unsigned size = 0; + double* data = nullptr; +public: + my_array() = default; + my_array(unsigned n) { + std::cout << "Constructing: " << data << std::endl; + // TODO: Implement constructor + } + + // Copy constructor + my_array(my_array const& other) { + std::cout << "Copy constructing: " << data << std::endl; + // TODO: Implement copy constructor + } + + // Copy assignment operator + my_array& operator=(my_array const& other) { + std::cout << "Destroying: " << data << std::endl; + // TODO: Implement copy assignment operator + std::cout << "Copy assigning: " << data << std::endl; + } + + // Move constructor + my_array(my_array&& other) noexcept { + std::cout << "Move construct: " << data << std::endl; + // TODO: Implement move constructor + } + + // Move assignment operator + my_array& operator=(my_array&& other) noexcept { + std::cout << "Move assign: " << data << std::endl; + // TODO: Implement move assignment operator + } + + // Destructor + ~my_array() { + std::cout << "Destroying: " < +#include + +struct A { + void printA() { std::cout << "A struct...." << std::endl; } +}; + +int main() { + +// https://en.cppreference.com/w/cpp/memory/unique_ptr + +// TODO: Define a unique pointer of A called p1 + + +p1->printA(); + +// displays address of the containing pointer +std::cout << p1.get() << std::endl; + +// TODO: Make a new unique pointer of A p2 +// Set it equal to p1 +// What happens when you compile this? + +// TODO: Use move to move p1 into p2 instead, what happens? + +p2->printA(); +std::cout << p1.get() << std::endl; +std::cout << p2.get() << std::endl; + + return 0; +} \ No newline at end of file diff --git a/exercises/6.2-special-pointers/part2/Makefile b/exercises/6.2-special-pointers/part2/Makefile new file mode 100644 index 0000000..5aa8edc --- /dev/null +++ b/exercises/6.2-special-pointers/part2/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 + +pointers : shared.o + $(CXX) $^ -o $@ + +run : shared + ./shared + +clean : + rm -rf *.o shared \ No newline at end of file diff --git a/exercises/6.2-special-pointers/part2/shared.cpp b/exercises/6.2-special-pointers/part2/shared.cpp new file mode 100644 index 0000000..5844b3e --- /dev/null +++ b/exercises/6.2-special-pointers/part2/shared.cpp @@ -0,0 +1,38 @@ +#include +#include + +struct A { + void printA() { std::cout << "A struct...." << std::endl; } +}; + +int main() { + +// https://en.cppreference.com/w/cpp/memory/shared_ptr + +// TODO: Create a shared pointer of A called p1 + + +// Printing the address of the managed object +std::cout << p1.get() << std::endl; +p1->printA(); + +// Creating a new shared pointer p2 that shares ownership of p1 + +p2->printA(); + +// Printing addresses of p1 and p2 +std::cout << p1.get() << std::endl; +std::cout << p2.get() << std::endl; + +// TODO: Print the use count of p1 and p2 + +// TODO: Look up how to relinquishes ownership of p1 + +std::cout << p1.get() << std::endl; + +// TODO: Print the use count of p1 + +std::cout << p2.get() << std::endl; + +return 0; +} \ No newline at end of file diff --git a/exercises/6.3-morton-order/instructions.md b/exercises/6.3-morton-order/instructions.md index 07014ca..05fbeec 100644 --- a/exercises/6.3-morton-order/instructions.md +++ b/exercises/6.3-morton-order/instructions.md @@ -1,11 +1,15 @@ + # Morton order matrices in C++ -## Rupert Nash -## r.nash@epcc.ed.ac.uk +## James Richings +## j.richings@epcc.ed.ac.uk + + +# Sample code Source for this can be obtained from Github. Get a new copy with: ``` -git clone https://github.com/EPCCed/archer2-CPP-2021-07-20 +git clone https://github.com/EPCCed/archer2-cpp ``` or update your existing one with @@ -17,15 +21,17 @@ git pull then you can ``` -cd archer2-CPP-2021-07-20/exercises/morton-order +cd archer2-cpp/exercises/morton-order ``` +# Morton ordering + The Morton ordering (or z-ordering) of a matrix lays out the elements along a recursive z-shaped curve, as shown in the figure of four iterations of the Z-order curve (from -[Wikipedia](https://en.wikipedia.org/wiki/Z-order_curve)). +[z ordered curve](https://en.wikipedia.org/wiki/Z-order_curve)). -![Morton order](mortonorder.png) +# Indices You can compute the Morton index `z` from the x- and y-indices (`i` and `j` respectively) by interleaving their bits. An example is shown @@ -33,10 +39,10 @@ in the table. | | 0 | 1 | 2 | 3 | |----|----|----|----|----| -| 0 | 0| 1| 4| 5| -| 1 | 2| 3| 6| 7| -| 2 | 8| 9| 12| 13| -| 3 | 10| 11| 14| 15| +| 0 | 0| 1| 4| 5 | +| 1 | 2| 3| 6| 7| +| 2 | 8| 9| 12| 13| +| 3 | 10| 11| 14| 15| Mapping between `x-y` indexes and Morton index for a 4 by 4 matrix. Decimal on the left and binary on the right. @@ -51,6 +57,8 @@ matrix. Decimal on the left and binary on the right. Mapping between `x-y` indexes and Morton index for a matrix of size 4-by-4. Decimal on the left and binary on the right. + + The advantage of laying out data in this way is that it improves data locality (and hence cache use) without having to tune a block size or similar parameter. On a modern multilevel cache machine[^1], this @@ -76,7 +84,7 @@ number. Go to the step 1 directory: ```bash -cd archer2-CPP-2021-07-20/exercises/morton-order/step1 +cd archer2-cpp/exercises/morton-order/step1 ``` Using the partial implemenation in `matrix.hpp`, your task is to @@ -95,7 +103,7 @@ supplied `Makefile` should work. Go to the step 2 directory: ```bash -cd archer2-CPP-2021-07-20/exercises/morton-order/step2 +cd archer2-cpp/exercises/morton-order/step2 ``` I have a potential solution to part 1 here, but feel free to copy your diff --git a/exercises/6.3-morton-order/step1/matrix.hpp b/exercises/6.3-morton-order/step1/matrix.hpp index ef94352..e8ea04e 100644 --- a/exercises/6.3-morton-order/step1/matrix.hpp +++ b/exercises/6.3-morton-order/step1/matrix.hpp @@ -2,7 +2,6 @@ #define MORTON_MATRIX_HPP #include -#include #include "bits.hpp" namespace morton { @@ -21,12 +20,12 @@ namespace morton { template class matrix { public: - // Default initialisation of unique_ptr is OK + // TODO - anything needed? matrix() : _rank(0) { } + // TODO - allocate some memory matrix(uint32_t r) - : _rank(r), _data(new T[r*r]) { // Check it's a power of 2. Could consider throwing an // exception, but these are not in the syllabus! @@ -38,19 +37,17 @@ namespace morton { matrix& operator=(const matrix& other) = delete; // Moving is allowed - // Default is ok because of choice to use unique_ptr to manage data storage - matrix(matrix&& other) noexcept = default; - matrix& operator=(matrix&& other) noexcept = default; + // TODO - will the default implementations be OK? + matrix(matrix&& other) noexcept; + matrix& operator=(matrix&& other) noexcept; // Destructor - // Default ok because of unique_ptr - ~matrix() = default; + // TODO - will the default implemenation be OK? + ~matrix(); // Create a new matrix with contents copied from this one matrix duplicate() const { - matrix ans(_rank); - std::copy(data(), data() + size(), ans.data.get()); - return ans; + // TODO } // Get rank size @@ -63,33 +60,25 @@ namespace morton { return uint64_t(_rank) * uint64_t(_rank); } + // TODO // Const element access - const T& operator()(uint32_t i, uint32_t j) const { - auto z = encode(i, j); - return _data[z]; - } + const T& operator()(uint32_t i, uint32_t j) const; + // TODO // Mutable element access - T& operator()(uint32_t i, uint32_t j) { - auto z = encode(i, j); - return _data[z]; - } + T& operator()(uint32_t i, uint32_t j); + // TODO // Raw data access (const and mutable versions) - const T* data() const { - return _data.get(); - } - T* data() { - return _data.get(); - } + const T* data() const; + T* data(); private: // rank of matrix uint32_t _rank; // Data storage - // Note using array version of unique_ptr - std::unique_ptr _data; + // TODO - choose how to store data and manage that memory }; } diff --git a/exercises/6.3-morton-order/step2/matrix.hpp b/exercises/6.3-morton-order/step2/matrix.hpp index d67715a..3754dd1 100644 --- a/exercises/6.3-morton-order/step2/matrix.hpp +++ b/exercises/6.3-morton-order/step2/matrix.hpp @@ -87,20 +87,19 @@ namespace morton { return _data.get(); } + // TODO: implement functions to get iterators to first and + // just-past-the-last elements in the matrix // Mutable iterators iterator begin() { - return iterator(data(), data()); } iterator end() { - return iterator(data(), data() + size()); } + // TODO: as above, but const // Const iterators const_iterator begin() const { - return const_iterator(data(), data()); } const_iterator end() const { - return const_iterator(data(), data() + size()); } private: @@ -128,27 +127,25 @@ namespace morton { public std::iterator { public: + // TODO // Default constructor - matrix_iterator() : _start(nullptr), _ptr(nullptr) { - } + matrix_iterator(); // Note: must provide copy c'tor, copy assign - // Defaults are fine + // TODO: Decide if the default copy/move/destruct behaviour is + // going to be OK. + // TODO // Get the x/y coordinates of the current element uint32_t x() const { - auto z = _ptr - _start; - return pack(z); } uint32_t y() const { - auto z = _ptr - _start; - return pack(z >> 1); } // Comparison operators. Note these are inline non-member friend // functions. friend bool operator==(const matrix_iterator& a, const matrix_iterator& b) { - return a._ptr == b._ptr; + // TODO } // Note this can be done in terms of the above friend bool operator!=(const matrix_iterator& a, const matrix_iterator& b) { @@ -157,33 +154,30 @@ namespace morton { // Dereference operator T& operator*() { - return *_ptr; + // TODO } // Preincrement operator matrix_iterator& operator++() { - ++_ptr; - return *this; + // TODO } + // TODO // Predecrement operator matrix_iterator& operator--() { - --_ptr; - return *this; + // TODO } private: - matrix_iterator(T* start, T* current) : _start(start), _ptr(current) { - } + // TODO: declare and define appropriate constructor(s) to create + // iterators pointing into a matrix's data. + // matrix_iterator(...); // Other constructors should probably not be publicly visible, so // we need to allow matrix access. friend matrix::type>; + // TODO: Define data members as needed - // We need the pointer to the first element to work out where we - // are in the matrix. - T* _start; - T* _ptr; }; } diff --git a/exercises/7-inheritance/part1/Makefile b/exercises/7-inheritance/part1/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/7-inheritance/part1/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/7-inheritance/part1/complex.cpp b/exercises/7-inheritance/part1/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/7-inheritance/part1/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/7-inheritance/part1/complex.hpp b/exercises/7-inheritance/part1/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/7-inheritance/part1/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/7-inheritance/part1/test.cpp b/exercises/7-inheritance/part1/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/7-inheritance/part1/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file diff --git a/exercises/7-inheritance/part2/Makefile b/exercises/7-inheritance/part2/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/7-inheritance/part2/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/7-inheritance/part2/complex.cpp b/exercises/7-inheritance/part2/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/7-inheritance/part2/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/7-inheritance/part2/complex.hpp b/exercises/7-inheritance/part2/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/7-inheritance/part2/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/7-inheritance/part2/test.cpp b/exercises/7-inheritance/part2/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/7-inheritance/part2/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file diff --git a/exercises/7-inheritance/part3/Makefile b/exercises/7-inheritance/part3/Makefile new file mode 100644 index 0000000..cec9ba2 --- /dev/null +++ b/exercises/7-inheritance/part3/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++14 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/7-inheritance/part3/complex.cpp b/exercises/7-inheritance/part3/complex.cpp new file mode 100644 index 0000000..f640699 --- /dev/null +++ b/exercises/7-inheritance/part3/complex.cpp @@ -0,0 +1,28 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + // Return real component +} + +/* Add definition of a member function to access the imaginary component */ + +Complex Complex::conj() { + // Return complex conjugate +} + +/* Add definition of 'norm' member function. Hint: Look up std::sqrt from the +cmath header to help calculate the magnitude of a complex number */ + +/* Add definition of 'add' member function */ + +bool Complex::equals(Complex other_complex) { + // Return true if the real and imaginary parts of the complex numbers are + // equal. False otherwise. +} \ No newline at end of file diff --git a/exercises/7-inheritance/part3/complex.hpp b/exercises/7-inheritance/part3/complex.hpp new file mode 100644 index 0000000..c7fda7b --- /dev/null +++ b/exercises/7-inheritance/part3/complex.hpp @@ -0,0 +1,37 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + /* Add declarations to create: + - A default constructor + - A constructor using just a real component + - A constructor using real and imaginary components + */ + + // Access components + double real(); + /* Add declaration to access the imaginary component */ + + // Compute the complex conjugate + Complex conj(); + + /* Add declaration for member function 'norm' that takes no arguments and + returns the magnitude of the complex number. + */ + + /* Add declaration for an 'add' member function as so: z = i.add(j) + I.e. For complex numbers i and j, z is the result of i + j. + */ + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + + /* Add private member variables to store the real and imaginary components of + the complex number. These should have type 'double' and a suitable default + value. + */ +}; + +#endif \ No newline at end of file diff --git a/exercises/7-inheritance/part3/test.cpp b/exercises/7-inheritance/part3/test.cpp new file mode 100644 index 0000000..fa707a9 --- /dev/null +++ b/exercises/7-inheritance/part3/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} \ No newline at end of file diff --git a/exercises/7-inheritance/part4/main.cpp b/exercises/7-inheritance/part4/main.cpp new file mode 100644 index 0000000..49d491d --- /dev/null +++ b/exercises/7-inheritance/part4/main.cpp @@ -0,0 +1,9 @@ +#include "poly.cpp" + + +int main() +{ +child x; + +x.predict(); +} diff --git a/exercises/7-inheritance/part4/poly.cpp b/exercises/7-inheritance/part4/poly.cpp new file mode 100644 index 0000000..44a2a15 --- /dev/null +++ b/exercises/7-inheritance/part4/poly.cpp @@ -0,0 +1,17 @@ +#include +#include "poly.hpp" + + +void base::forcast(){ + std::cout << "The forcast is Rain" << std::endl; +} + +void child::forcast(){ + std::cout << "The forcast is Sunny" << std::endl; +} + +void child::predict(){ + base::forcast(); +}; + + diff --git a/exercises/7-inheritance/part4/poly.hpp b/exercises/7-inheritance/part4/poly.hpp new file mode 100644 index 0000000..33ab206 --- /dev/null +++ b/exercises/7-inheritance/part4/poly.hpp @@ -0,0 +1,23 @@ + +class base +{ + +public: + +void forcast(); + +}; + + +class child : base +{ + +private: + +void forcast(); + +public: + +void predict(); + +}; diff --git a/exercises/8-algorithm/ex.cpp b/exercises/8-algorithm/ex.cpp index e4f5ba2..02848ee 100644 --- a/exercises/8-algorithm/ex.cpp +++ b/exercises/8-algorithm/ex.cpp @@ -26,12 +26,10 @@ int main(int argc, char* argv[]) { auto gen = []() { return std::rand() % 100; }; // Your code here.... - std::generate(begin(nums), end(nums), gen); // Now, sort nums. // Your code here.... - std::sort(nums.begin(), nums.end()); std::cout << "Sorted nums: "; print_vec(nums); @@ -39,14 +37,11 @@ int main(int argc, char* argv[]) { // algorithm function // Your code here.... - std::sort(nums.begin(), nums.end(), std::greater()); std::cout << "Reverse sorted nums (a): "; print_vec(nums); // Your code here.... - std::sort(nums.begin(), nums.end()); - std::reverse(nums.begin(), nums.end()); std::cout << "Reverse sorted nums (b): "; print_vec(nums); @@ -62,11 +57,7 @@ int main(int argc, char* argv[]) { auto fibs = std::vector(47); // Your code here.... - fibs[0] = 0; - fibs[1] = 1; - std::transform(fibs.begin(), fibs.end() - 2, fibs.begin() + 1, - fibs.begin() + 2, std::plus()); print_vec(fibs); @@ -76,8 +67,6 @@ int main(int argc, char* argv[]) { auto fibs_less = std::vector(); // Your code here.... - std::copy_if(fibs.begin(), fibs.end(), std::back_inserter(fibs_less), - [](int x) { return x <= 4000000; }); std::cout << "fibs <= 4000000: "; print_vec(fibs_less); @@ -87,15 +76,13 @@ int main(int argc, char* argv[]) { // Your code here.... - std::copy_if(fibs_less.begin(), fibs_less.end(), std::back_inserter(evens), - [](int x) { return !(x % 2); }); std::cout << "Evens: "; print_vec(evens); // Finally, let's sum them (hint: std::accumulate) - int sum = std::accumulate(evens.begin(), evens.end(), 0); + int sum = 0; std::cout << "Sum of even fibonacci numbers not greater than 4 million: " << sum << std::endl; diff --git a/exercises/README.md b/exercises/README.md index 95ab517..861b391 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -7,7 +7,7 @@ See each subdirectory for further instructions * [2.1 Class types](2.1-class-types/) * [2.2 Complex numbers](2.2-complex/) * [3 Containers](3-containers/) -* [4 My array](4-my-array/) +* [4 Pointers](4-pointers/) * [5 Templates](5-templates/) * [6.1 Pointers](6.1-pointers/) * [6.2 Special pointers](6.2-special-pointers/) diff --git a/lectures/0-course-intro-2-days/README.md b/lectures/0-course-intro-2-days/README.md new file mode 100644 index 0000000..2619842 --- /dev/null +++ b/lectures/0-course-intro-2-days/README.md @@ -0,0 +1,78 @@ +template: titleslide + +# Course Introduction +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk + +--- +template: titleslide +# Timetable + +--- +# Day 1 + +- 9:30 - 9:45 : Introduction to this course + +- 9:45 – 10:45 : Introduction to C++ + +- 10:45 – 11:00 : Coffee break + +- 11:00 – 12:30 : Class types + +- 12:30 – 1:30 : Lunch + +- 1:30 – 2:30 : loops, containers, and iterators + +- 2:30 - 3:00 : Managing resources + +- 3:00 – 3:15 : Coffee + +- 3:15 - 4:00 : RAII + +--- +# Day 2 + +- 9:30 - 9:45 : Recap previous day + +- 9:45 - 10:45 : Templates & traits for generic programming + +- 10:45 – 11:00 : Coffee + +- 11:00 – 12:30 : RAII continued + +- 12:30 - 1:30 : Lunch + +- 1:30 – 2:30 : Algorithms and lambdas + +- 2:30 - 3:00 : Linear algebra with Eigen + +- 3:00 - 3:15 : Coffee + +- 3:15 - 3:45 : Time to work on exercises from the course & ask questions. + +- 3:45 - 4:00 : Wrap-up + +--- +# Access to ARCHER2 + +- SAFE: https://safe.epcc.ed.ac.uk/ + +- SAFE Docs: https://epcced.github.io/safe-docs/ + +- ARCHER2 Docs: https://docs.archer2.ac.uk/user-guide/connecting/ + +- Project: ta205 + +---- + +Complete setup of machine account: +- Login to SAFE +- Set multi-factor token: + - `Login accounts -> user@archer2 -> Set MFA-Token` +- Connect via terminal: + - `ssh user@login.archer2.ac.uk` + - `ssh -i [path-to-ssh-key] user@login.archer2.ac.uk` +- Get LDAP password from SAFE (first login only): + - `Login accounts -> user@archer2 -> View Login Account Password` +- Set password (used for recovery only) + diff --git a/lectures/0-course-intro/index.html b/lectures/0-course-intro-2-days/index.html similarity index 100% rename from lectures/0-course-intro/index.html rename to lectures/0-course-intro-2-days/index.html diff --git a/lectures/0-course-intro/README.md b/lectures/0-course-intro-3-days/README.md similarity index 73% rename from lectures/0-course-intro/README.md rename to lectures/0-course-intro-3-days/README.md index 29ec8cf..f8cc6cc 100644 --- a/lectures/0-course-intro/README.md +++ b/lectures/0-course-intro-3-days/README.md @@ -77,14 +77,20 @@ template: titleslide - SAFE Docs: https://epcced.github.io/safe-docs/ +- ARCHER2 Docs: https://docs.archer2.ac.uk/user-guide/connecting/ + - Project: ta198 -- Machine account: - - Login to safe - - Login accounts -> Request a login account - - Set user name - - Set ssh key - - Set multi-factor token - - ssh user@login.archer2.ac.uk - - Set password (used for recovery only) +---- + +Complete setup of machine account: +- Login to SAFE +- Set multi-factor token: + - `Login accounts -> user@archer2 -> Set MFA-Token` +- Connect via terminal: + - `ssh user@login.archer2.ac.uk` + - `ssh -i [path-to-ssh-key] user@login.archer2.ac.uk` +- Get LDAP password from SAFE (first login only): + - `Login accounts -> user@archer2 -> View Login Account Password` +- Set password (used for recovery only) diff --git a/lectures/0-course-intro-3-days/index.html b/lectures/0-course-intro-3-days/index.html new file mode 100644 index 0000000..e7a0665 --- /dev/null +++ b/lectures/0-course-intro-3-days/index.html @@ -0,0 +1,10 @@ + + + +Course Introduction + + + + + + diff --git a/lectures/1-cpp-intro/README.md b/lectures/1-cpp-intro/README.md index 59964eb..538ea58 100644 --- a/lectures/1-cpp-intro/README.md +++ b/lectures/1-cpp-intro/README.md @@ -1,8 +1,8 @@ template: titleslide # A brief introduction to C++ -## Nathan Mannall, EPCC -## n.mannall@epcc.ed.ac.uk +## James Richings, EPCC +## j.richings@epcc.ed.ac.uk --- @@ -60,6 +60,19 @@ styles that people have developed over the decades. Please ask questions any time! +--- +# A bit of history + +- C++ was originally designed as an extension of C + - Well written C tends to be legal C++ but C is not a subset of C++ + - There are programs that are valid C but not valid C++ + - However, C++ supports every programming technique supported by C + + +- Released in 1985, but first standardised in 1998 with C++98 + +- New features added to the standard over time + --- # The misunderstood monster @@ -86,20 +99,19 @@ https://commons.wikimedia.org/w/index.php?curid=3558176 - "Expert friendly" --- -# Octopus vs Swiss Army knife +# The misunderstood monster -> "C++ is an octopus made by nailing extra legs onto a dog" - Steve Taylor +- However! -.center[![:scale_img 40%](octodog.jpg) ![:scale_img 40%](sak.jpg)] +- We can pick and choose the parts of the standard we want to use -But you can cut off some extra legs to get the right dog for your -program! +- Not expected to know "all of C++" + +- Pick a subset, write some code, and gradually learn more of the language, its libraries, and its tools ??? -Why is it called C++20? -Because that's how many legs they had to nail on to "fix" the octopus. --- # The philosophy of C++ (community) @@ -119,17 +131,19 @@ Because that's how many legs they had to nail on to "fix" the octopus. --- # C++ is alive! -C++ is a work in progress. +- C++ is a work in progress. -Every three years there is a new update to the International Standard +- Every three years there is a new update to the International Standard (since C++11) -C++20 is only fully supported by MSVC (although GCC is nearly there). Major +- C++20 is only fully supported by MSVC (although GCC is nearly there). Major new features are ranges, coroutines, concepts, and modules -Latest one, C++23 has been released. Major features include +- Latest one, C++23 has been released. Major features include networking, string formatting, executors, and consolidation of new C++20 features +- Good summary of compiler support here: https://en.cppreference.com/w/cpp/compiler_support + --- # References - Bjarne Stroustrup, "Programming: Principles and Practice Using C++" diff --git a/lectures/2-classes/README.md b/lectures/2-classes/README.md index 9574d90..2e12f2a 100644 --- a/lectures/2-classes/README.md +++ b/lectures/2-classes/README.md @@ -1,7 +1,7 @@ template: titleslide # Classes -## Nathan Mannall, EPCC -## n.mannall@epcc.ed.ac.uk +## Luca Parisi, EPCC +## l.parisi@epcc.ed.ac.uk --- @@ -851,6 +851,23 @@ Refer back to the last slide codes and ask mutable reference then it would be a mutable ref while `auto const&` would have stayed constant) +--- +template: titleslide +# Exercise + +--- +# Note: Complex numbers + +- A complex number is a number that can be expressed in the form `a+bi` where: + - `a` is the real part + - `b` is the imaginary part + - `i` is the imaginary unit defined as the square root of `-1` +- We can define basic operations for two complex numbers `a=x+yi` and `b=u+vi` as: + - `a + b = (x + yi) + (u + vi) = (x + u) + (y + v)i` + - `a - b = (x + yi) - (u + vi) = (x - u) + (y - v)i` + +.center[![:scale_img 40%](complex_numbers.svg)] + --- # Exercise @@ -869,9 +886,9 @@ complex number class and `test.cpp` holds some basic unit tests. You can compile and run with: ``` $ make && ./test -c++ --std=c++14 -I../include -c -o complex.o complex.cpp -c++ --std=c++14 -I../include -c -o test.o test.cpp -c++ complex.o test.o -o test +g++ --std=c++14 -I../include -c -o complex.o complex.cpp +g++ --std=c++14 -I../include -c -o test.o test.cpp +g++ complex.o test.o -o test =============================================================================== All tests passed (34 assertions in 6 test cases) ``` diff --git a/lectures/2-classes/complex_numbers.svg b/lectures/2-classes/complex_numbers.svg new file mode 100644 index 0000000..89b0409 --- /dev/null +++ b/lectures/2-classes/complex_numbers.svg @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lectures/3-loops-containers/README.md b/lectures/3-loops-containers/README.md index f9477ee..2cf2ce1 100644 --- a/lectures/3-loops-containers/README.md +++ b/lectures/3-loops-containers/README.md @@ -486,4 +486,4 @@ Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), a function (`AddWord`) that adds a string to a `std::map` as the key, the value being the length of the string. Note: Copy your completed `vector_ex.cpp`/`.hpp` files from part 1. -You will want to find the documentatation for `map` on https://en.cppreference.com/ +You can find documentatation for `map` here: https://en.cppreference.com/ diff --git a/lectures/4-resources/README.md b/lectures/4-resources/README.md index a6ed27f..c47f2e7 100644 --- a/lectures/4-resources/README.md +++ b/lectures/4-resources/README.md @@ -1,8 +1,8 @@ template: titleslide # Resource management -## Nathan Mannall, EPCC -## n.mannall@epcc.ed.ac.uk +## Luca Parisi, EPCC +## l.parisi@epcc.ed.ac.uk --- # Resources @@ -71,6 +71,20 @@ allocation: the *heap* You need to request some memory to store an object and then give it back when you are finished +In C++ this uses the `new` and `delete` keywords + +```C++ +int main() +{ + // This memory for 3 integers + // is allocated on heap. + int *ptr = new int[3]{1,2,3}; + + // To keep things tidy we need to manually call delete + delete[] ptr; +} +``` + ??? Fortran has allocatable arrays and somewhat restricted pointers @@ -146,6 +160,22 @@ has ended and accessing it is UB In C++ you can manually create instances of objects dynamically with `new` and try to remember to destroy them with `delete` +```C++ +int main() +{ + // Memory for an integer is allocated on heap. + int *ptr = new int{}; + *ptr = 7; // Put a value in that memory location + + // Initialize a dynamic array + int* array_ptr = new int[5]{ 9, 7, 5, 3, 1 }; + + // To keep things tidy we need to manually call delete + delete ptr; + delete[] array_ptr; +} +``` + But doing so is a recipe for problems! ??? diff --git a/lectures/5-templates/README.md b/lectures/5-templates/README.md index 53cd8dd..4b0b92d 100644 --- a/lectures/5-templates/README.md +++ b/lectures/5-templates/README.md @@ -1,7 +1,7 @@ template: titleslide -# Templates -## Nathan Mannall -## n.mannall@epcc.ed.ac.uk +# Templates and Traits +## Luca Parisi, EPCC +## l.parisi@epcc.ed.ac.uk ??? @@ -340,6 +340,151 @@ copy add a reference `&` The `auto` keyword follows the same rules as template argument deduction +--- +template: titleslide + +# Traits +--- + +# Type traits + +- Important C++ generic programming technique, used across the + standard library + +- The "if-then-else" of types + +- Provide a template class that has typedefs/member functions that + specify the configurable parts + +- Your generic algorithm then uses this class, specialised on the type + it is working on, to select behaviour + +- You do not have to change the source of either the algorithm or the + working type + +--- + +# STL traits + +Several headers contain these: + +- The header `` has lots of information for handling + types. E.g. `std::is_pointer::value` has value of false. + +- `std::numeric_limits` gives lots of parameters for the built in + number types, such as largest and smallest values, whether they are + integer or floating types, etc. + +Other traits are used everywhere behind the scenes for efficiency. + +--- + +# Real example + +Suppose you are writing a wrapper around MPI to allow use without +having to always say `MPI_FLOAT` - since the compiler knows the types +already! + +```C++ +class Comm { +public: + template + void send(const std::vector& data, + int dest, int tag); +}; + +auto& world = Mpi::CommWorld(); +std::vector data = {2, 4, 6, 8}; + +world.send(data, other_rank, tag); +``` + +--- + +# Real example + +Simplified implementation: + +```C++ +template +void Comm::send(const std::vector& data, int dest, int tag) { + MPI_Send(reinterpret_cast(data.data()), data.size(), + DATATYPE, + dest, tag, m_comm); +} +``` + +-- +How to fill in the right type? + +For the types in the standard, we could +provide a specialisation for each one: + +```C++ +template <> +void Comm::send(const std::vector& data, int dest, int tag) { + MPI_Send(reinterpret_cast(data.data()), data.size(), + MPI_INT, + dest, tag, m_comm); +} +``` + +--- + +# Real example + +But there are lots of types (say `M`) and lots of MPI calls (say `N`) +to be wrapped, so we have to write `M * N` specialisations... + +Also how to add any custom types? User would have to mess around with +our library. + +--- +# Real example + +Create a traits class that tells our functions how to handle different +types: + +```C++ +template +struct DataTypeTraits { + // Don't provide a default definition + // (because there is no generic way to make an MPI datatype) + static MPI_Datatype Get(); +}; + +template +void Comm::send(const std::vector& data, int dest, int tag) { + MPI_Send(reinterpret_cast(data.data()), data.size(), + DataTypeTraits::Get(), + dest, tag, m_comm); +} +``` + +--- + +# Real example + +Then we can then provide a specialised definition for all the types we +can handle: + +```C++ +template<> +MPI_Datatype DataTypeTraits::Get() { + return MPI_INT; +} + +template<> +MPI_Datatype DataTypeTraits::Get() { + return MPI_FLOAT; +} + +// etc +``` + +If we try to communicate a data type we haven't specialised for, we +will get a compile time error! + --- template: titleslide # Exercise diff --git a/lectures/5-templates/index.html b/lectures/5-templates/index.html index 30abb8a..1b9383f 100644 --- a/lectures/5-templates/index.html +++ b/lectures/5-templates/index.html @@ -1,7 +1,7 @@ -C++ Templates +Templates and Traits diff --git a/lectures/6.1-RAII/README.md b/lectures/6.1-RAII/README.md index 7ad4338..4cf7c75 100644 --- a/lectures/6.1-RAII/README.md +++ b/lectures/6.1-RAII/README.md @@ -1,97 +1,8 @@ template: titleslide -# Managing resources -## James Richings, EPCC -## j.richings@epcc.ed.ac.uk - ---- -# Resources - -In a program you often need to manage things like: - -- memory - -- open files - -- GPUs - -- network sockets - -??? - -Let's talk a bit about memory first - ---- -# Memory: overview - -.columns[ -.col[ -Modern OSes given each process a flat address space - -Each byte can be accessed by its address, which is just a number. - -For most purposes this can be considered in two parts: -- stack -- heap -] -.col[ -.center[ -![:scale_img 50%](mem_layout.jpg) -] -] -] - ---- -# Memory: stack - -In C++ (and C and many other compiled languages) local variables are -stored in the **stack**. - -As your program runs, the local variables that are defined, get space -allocated by the compiler relative to the current stack frame. - -Each time you call a function, you start a new stack frame by -incrementing the stack pointer. - -Variables are implemented as offsets from this, so allocating a local -variable has no run-time cost. - -When you return from a function, the stack pointer is updated and -deallocation also has no cost - -These allocations are called *static* because they have to be prepared -at compile time - ---- -# Memory: heap - -The language and OS also make available some memory for dynamic -allocation: the *heap* - -You need to request some memory to store an object and then give it -back when you are finished. - -```C++ -int main() -{ - // This memory for 3 integers - // is allocated on heap. - int *ptr = new int[3]{1,2,3}; - - // To keep things tidy we need to manually call delete - delete[] ptr; -} -``` - - - -??? - -Fortran has allocatable arrays and somewhat restricted pointers - -C programmers will be familiar with malloc and free, which is also -present in C++, but should never be used (I can't recall ever having -done so) +# RAII +## Nathan Mannall, EPCC +## n.mannall@epcc.ed.ac.uk --- template: titleslide diff --git a/lectures/6.1-RAII/index.html b/lectures/6.1-RAII/index.html index bdcc5ee..2ebd218 100644 --- a/lectures/6.1-RAII/index.html +++ b/lectures/6.1-RAII/index.html @@ -1,7 +1,7 @@ -Resource Management +RAII diff --git a/lectures/6.2-RAII-cont/README.md b/lectures/6.2-RAII-smart-pointers/README.md similarity index 99% rename from lectures/6.2-RAII-cont/README.md rename to lectures/6.2-RAII-smart-pointers/README.md index e672d1c..079b682 100644 --- a/lectures/6.2-RAII-cont/README.md +++ b/lectures/6.2-RAII-smart-pointers/README.md @@ -1,8 +1,8 @@ template: titleslide -# RAII continued -## James Richings, EPCC -## j.richings@epcc.ed.ac.uk +# RAII - Smart Pointers +## Nathan Mannall, EPCC +## n.mannall@epcc.ed.ac.uk --- diff --git a/lectures/6.2-RAII-cont/index.html b/lectures/6.2-RAII-smart-pointers/index.html similarity index 84% rename from lectures/6.2-RAII-cont/index.html rename to lectures/6.2-RAII-smart-pointers/index.html index bdcc5ee..1d2d391 100644 --- a/lectures/6.2-RAII-cont/index.html +++ b/lectures/6.2-RAII-smart-pointers/index.html @@ -1,7 +1,7 @@ -Resource Management +RAII - Smart Pointers diff --git a/lectures/6.2-RAII-cont/mem_layout.jpg b/lectures/6.2-RAII-smart-pointers/mem_layout.jpg similarity index 100% rename from lectures/6.2-RAII-cont/mem_layout.jpg rename to lectures/6.2-RAII-smart-pointers/mem_layout.jpg diff --git a/lectures/6.2-RAII-cont/sample/.gitignore b/lectures/6.2-RAII-smart-pointers/sample/.gitignore similarity index 100% rename from lectures/6.2-RAII-cont/sample/.gitignore rename to lectures/6.2-RAII-smart-pointers/sample/.gitignore diff --git a/lectures/6.2-RAII-cont/sample/Makefile b/lectures/6.2-RAII-smart-pointers/sample/Makefile similarity index 100% rename from lectures/6.2-RAII-cont/sample/Makefile rename to lectures/6.2-RAII-smart-pointers/sample/Makefile diff --git a/lectures/6.2-RAII-cont/sample/arr1.cpp b/lectures/6.2-RAII-smart-pointers/sample/arr1.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/arr1.cpp rename to lectures/6.2-RAII-smart-pointers/sample/arr1.cpp diff --git a/lectures/6.2-RAII-cont/sample/arr2.cpp b/lectures/6.2-RAII-smart-pointers/sample/arr2.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/arr2.cpp rename to lectures/6.2-RAII-smart-pointers/sample/arr2.cpp diff --git a/lectures/6.2-RAII-cont/sample/arr3.cpp b/lectures/6.2-RAII-smart-pointers/sample/arr3.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/arr3.cpp rename to lectures/6.2-RAII-smart-pointers/sample/arr3.cpp diff --git a/lectures/6.2-RAII-cont/sample/dyn1.cpp b/lectures/6.2-RAII-smart-pointers/sample/dyn1.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/dyn1.cpp rename to lectures/6.2-RAII-smart-pointers/sample/dyn1.cpp diff --git a/lectures/6.2-RAII-cont/sample/dyn2.cpp b/lectures/6.2-RAII-smart-pointers/sample/dyn2.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/dyn2.cpp rename to lectures/6.2-RAII-smart-pointers/sample/dyn2.cpp diff --git a/lectures/6.2-RAII-cont/sample/dyn3.cpp b/lectures/6.2-RAII-smart-pointers/sample/dyn3.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/dyn3.cpp rename to lectures/6.2-RAII-smart-pointers/sample/dyn3.cpp diff --git a/lectures/6.2-RAII-cont/sample/shared.cpp b/lectures/6.2-RAII-smart-pointers/sample/shared.cpp similarity index 100% rename from lectures/6.2-RAII-cont/sample/shared.cpp rename to lectures/6.2-RAII-smart-pointers/sample/shared.cpp diff --git a/lectures/8-algorithms-lambdas-traits/README.md b/lectures/8-algorithms-lambdas/README.md similarity index 76% rename from lectures/8-algorithms-lambdas-traits/README.md rename to lectures/8-algorithms-lambdas/README.md index 83f471b..ae88770 100644 --- a/lectures/8-algorithms-lambdas-traits/README.md +++ b/lectures/8-algorithms-lambdas/README.md @@ -1,8 +1,8 @@ template: titleslide -# Algorithms, lambdas, traits -## Nathan Mannall -## n.mannall@epcc.ed.ac.uk +# Algorithms and lambdas +## Luca Parisi, EPCC +## l.parisi@epcc.ed.ac.uk --- @@ -409,151 +409,6 @@ int main(int argc, char** argv) { # Results .center[![-O2](looptests/opt2.svg)] ---- -template: titleslide - -# Traits ---- - -# Type traits - -- Important C++ generic programming technique, used across the - standard library - -- The "if-then-else" of types - -- Provide a template class that has typedefs/member functions that - specify the configurable parts - -- Your generic algorithm then uses this class, specialised on the type - it is working on, to select behaviour - -- You do not have to change the source of either the algorithm or the - working type - ---- - -# STL traits - -Several headers contain these: - -- The header `` has lots of information for handling - types. E.g. `std::is_pointer::value` has value of false. - -- `std::numeric_limits` gives lots of parameters for the built in - number types, such as largest and smallest values, whether they are - integer or floating types, etc. - -Other traits are used everywhere behind the scenes for efficiency. - ---- - -# Real example - -Suppose you are writing a wrapper around MPI to allow use without -having to always say `MPI_FLOAT` - since the compiler knows the types -already! - -```C++ -class Comm { -public: - template - void send(const std::vector& data, - int dest, int tag); -}; - -auto& world = Mpi::CommWorld(); -std::vector data = {2, 4, 6, 8}; - -world.send(data, other_rank, tag); -``` - ---- - -# Real example - -Simplified implementation: - -```C++ -template -void Comm::send(const std::vector& data, int dest, int tag) { - MPI_Send(reinterpret_cast(data.data()), data.size(), - DATATYPE, - dest, tag, m_comm); -} -``` - --- -How to fill in the right type? - -For the types in the standard, we could -provide a specialisation for each one: - -```C++ -template <> -void Comm::send(const std::vector& data, int dest, int tag) { - MPI_Send(reinterpret_cast(data.data()), data.size(), - MPI_INT, - dest, tag, m_comm); -} -``` - ---- - -# Real example - -But there are lots of types (say `M`) and lots of MPI calls (say `N`) -to be wrapped, so we have to write `M * N` specialisations... - -Also how to add any custom types? User would have to mess around with -our library. - ---- -# Real example - -Create a traits class that tells our functions how to handle different -types: - -```C++ -template -struct DataTypeTraits { - // Don't provide a default definition - // (because there is no generic way to make an MPI datatype) - static MPI_Datatype Get(); -}; - -template -void Comm::send(const std::vector& data, int dest, int tag) { - MPI_Send(reinterpret_cast(data.data()), data.size(), - DataTypeTraits::Get(), - dest, tag, m_comm); -} -``` - ---- - -# Real example - -Then we can then provide a specialised definition for all the types we -can handle: - -```C++ -template<> -MPI_Datatype DataTypeTraits::Get() { - return MPI_INT; -} - -template<> -MPI_Datatype DataTypeTraits::Get() { - return MPI_FLOAT; -} - -// etc -``` - -If we try to communicate a data type we haven't specialised for, we -will get a compile time error! - --- # Algorithms Exercise diff --git a/lectures/8-algorithms-lambdas-traits/index.html b/lectures/8-algorithms-lambdas/index.html similarity index 90% rename from lectures/8-algorithms-lambdas-traits/index.html rename to lectures/8-algorithms-lambdas/index.html index 868058d..831e141 100644 --- a/lectures/8-algorithms-lambdas-traits/index.html +++ b/lectures/8-algorithms-lambdas/index.html @@ -1,7 +1,7 @@ -Algorithms, lambdas, traits +Algorithms and lambdas diff --git a/lectures/8-algorithms-lambdas-traits/looptests/opt0.svg b/lectures/8-algorithms-lambdas/looptests/opt0.svg similarity index 100% rename from lectures/8-algorithms-lambdas-traits/looptests/opt0.svg rename to lectures/8-algorithms-lambdas/looptests/opt0.svg diff --git a/lectures/8-algorithms-lambdas-traits/looptests/opt2.svg b/lectures/8-algorithms-lambdas/looptests/opt2.svg similarity index 100% rename from lectures/8-algorithms-lambdas-traits/looptests/opt2.svg rename to lectures/8-algorithms-lambdas/looptests/opt2.svg diff --git a/lectures/9-eigen/README.md b/lectures/9-eigen/README.md index f2cb0c0..674523a 100644 --- a/lectures/9-eigen/README.md +++ b/lectures/9-eigen/README.md @@ -1,7 +1,7 @@ template: titleslide # Linear Algebra for C++ (using Eigen) -## James Richings, EPCC -## j.richings@epcc.ed.ac.uk +## Nathan Mannall, EPCC +## n.mannall@epcc.ed.ac.uk --- # Source diff --git a/lectures/README.md b/lectures/README.md index c5861e1..0db5040 100644 --- a/lectures/README.md +++ b/lectures/README.md @@ -1,14 +1,15 @@ # Lectures -* [Course introduction](0-course-intro) +* [Course introduction (2 days)](0-course-intro-2-days) +* [Course introduction (3 days)](0-course-intro-3-days) * [A brief introduction to C++](1-cpp-intro) * [Class types](2-classes) * [Loops, containers, and iterators](3-loops-containers) * [Managing resources](4-resources) * [RAII](6.1-RAII) -* [Templates for generic programming](5-templates) -* [RAII continued](6.2-RAII-cont) -* [Combining Classes](7-combining-classes) -* [Algorithms, lambdas and traits](8-algorithms-lambdas-traits) +* [Templates & traits for generic programming](5-templates) +* [RAII - Smart pointers](6.2-RAII-smart-pointers) +* [Combining classes](7-combining-classes) +* [Algorithms and lambdas](8-algorithms-lambdas) * [Linear algebra with Eigen](9-eigen) * [Threads with C++](10.1-threads) and [Further topics with threads](10.2-threads-cont) diff --git a/solutions/.gitignore b/solutions/.gitignore new file mode 100644 index 0000000..b443814 --- /dev/null +++ b/solutions/.gitignore @@ -0,0 +1,17 @@ +# Ignore executables created by the solutions +2.1-class-types/test +2.2-complex/test +3-containers/part1/test +3-containers/part2/test +5-templates/part1/sum +5-templates/part2/test +6.1-my-array/part*/my_array +6.2-special-pointers/part1/unique +6.2-special-pointers/part2/shared +8-algorithm/ex +9-eigen/explicit +9-eigen/implicit +9-eigen/sparse +9-eigen/*.txt +9-eigen/*.gif +10-threads/area \ No newline at end of file diff --git a/solutions/10-threads/Makefile b/solutions/10-threads/Makefile new file mode 100644 index 0000000..e01548c --- /dev/null +++ b/solutions/10-threads/Makefile @@ -0,0 +1,28 @@ +# Makefile for mandelbrot area code + +# +# C compiler and options for Intel +# +CC= g++ +CFLAGS = -O2 -std=c++11 +LIB= -lm -lpthread + +# +# Object files +# +OBJ= area.o + +# +# Compile +# +area: $(OBJ) + $(CC) -o $@ $(OBJ) $(LIB) + +.cpp.o: + $(CC) $(CFLAGS) -c $< + +# +# Clean out object files and the executable. +# +clean: + rm *.o area diff --git a/exercises/kokkos/README.md b/solutions/10-threads/README.md similarity index 100% rename from exercises/kokkos/README.md rename to solutions/10-threads/README.md diff --git a/solutions/10-threads/area.cpp b/solutions/10-threads/area.cpp new file mode 100644 index 0000000..3249dc7 --- /dev/null +++ b/solutions/10-threads/area.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +using complex = std::complex; + +// Determine if a complex number is inside the set +bool in_mandelbrot(const complex& c) { + const auto MAXITER = 2000; + auto z = c; + for (auto i = 0; i < MAXITER; ++i) { + z = z*z + c; + if (std::norm(z) > 4.0) + return false; + } + return true; +} + +void area( + int start, + int stop, + int npoints, + double scale_real, + double scale_imag, + int& num_inside, + std::mutex& mutex +) { + const auto eps = 1.0e-7; + const auto shift = complex{-2.0 + eps, 0.0 + eps}; + + for (int i = start; i < stop; ++i) { + for (int j = 0; j < npoints; ++j) { + const auto c = shift + complex{ + (scale_real * i) / npoints, + (scale_imag * j) / npoints + }; + if (in_mandelbrot(c)) { + mutex.lock(); + num_inside++; + mutex.unlock(); + } + } + } +} + +int main() { + const auto NPOINTS = 2000; + const auto scale_real = 2.5; + const auto scale_imag = 1.125; + + using clock = std::chrono::high_resolution_clock; + auto start = clock::now(); + + std::mutex mutex; + int num_inside = 0; + + int n_threads = 16; + std::vector threads; + + int points_per_thread = NPOINTS / n_threads; + int i = 0; + int j = 0; + + for (int id = 0; id < n_threads; ++id) { + i = id * points_per_thread; + j = i + points_per_thread; + threads.push_back( + std::thread( + area, i, j, NPOINTS, scale_real, scale_imag, std::ref(num_inside), std::ref(mutex) + ) + ); + } + + // Wait for all threads to finish + for (auto&& thread : threads) { + thread.join(); + } + + auto finish = clock::now(); + + // Calculate area and error and output the results + auto area = 2.0 * scale_real * scale_imag * double(num_inside) / double(NPOINTS * NPOINTS); + auto error = area / double(NPOINTS); + + std::printf("Area of Mandlebrot set = %12.8f +/- %12.8f\n", area, error); + auto dt = std::chrono::duration(finish-start); + std::printf("Time = %12.8f seconds\n", dt.count()); +} diff --git a/solutions/10-threads/instructions.md b/solutions/10-threads/instructions.md new file mode 100644 index 0000000..ff36f3d --- /dev/null +++ b/solutions/10-threads/instructions.md @@ -0,0 +1,32 @@ +# C++ threads +## Mark Bull +## m.bull@epcc.ed.ac.uk + +Source for this can be obtained from Github. Get a new copy with: + +``` +git clone https://github.com/EPCCed/archer2-CPP-2021-07-20 +``` + +or update your existing one with + +``` +git pull +``` + +then you can + +``` +cd archer2-CPP-2021-07-20/exercises/threads +``` + + + +`area.cpp` contains a C++ version of the Mandelbrot example which you used in Threaded Programming. + +Parallelise the outer loop of the main computation using C++ +threads. Try using either a function pointer or a lambda expression. + +Try different mechanisms for synchronising the update to the shared +counter: a mutex, and lock guard or an atomic int. + diff --git a/solutions/10-threads/instructions.pdf b/solutions/10-threads/instructions.pdf new file mode 100644 index 0000000..6eef464 Binary files /dev/null and b/solutions/10-threads/instructions.pdf differ diff --git a/solutions/2.1-class-types/Makefile b/solutions/2.1-class-types/Makefile new file mode 100644 index 0000000..da851f6 --- /dev/null +++ b/solutions/2.1-class-types/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++17 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/solutions/2.1-class-types/README.md b/solutions/2.1-class-types/README.md new file mode 100644 index 0000000..9760ac7 --- /dev/null +++ b/solutions/2.1-class-types/README.md @@ -0,0 +1,29 @@ +# Class types exercise + +In your clone of this repository, find the `2.1-class-types` exercise and list the files + +``` +$ cd archer2-cpp/exercises/2.1-class-types +$ ls +Makefile README.md complex.cpp complex.hpp test.cpp +``` + +The files `complex.hpp` and `complex.cpp` contain the beginings of a complex number class. Follow the instructions in the comments to complete the missing declarations in `complex.hpp` and then add the required out of line definitions in `complex.cpp`. + +To test your implementation, `test.cpp` holds some basic unit tests created using the Catch2 unit testing library. + +You can compile by running: + +``` +$ make +g++ --std=c++14 -I../include -c -o complex.o complex.cpp +g++ complex.o test.o -o test +``` + +Then run the tests using: + +``` +$ ./test +=============================================================================== +All tests passed (36 assertions in 5 test cases) +``` diff --git a/solutions/2.1-class-types/complex.cpp b/solutions/2.1-class-types/complex.cpp new file mode 100644 index 0000000..c6366d2 --- /dev/null +++ b/solutions/2.1-class-types/complex.cpp @@ -0,0 +1,32 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() { + return re; +} + +double Complex::imag() { + return im; +} + +Complex Complex::conj() { + return Complex{re, -im}; +} + +double Complex::norm() { + return std::sqrt(re*re + im*im); +} + +Complex Complex::add(Complex other_complex) { + return Complex{re + other_complex.re, im + other_complex.im}; +} + +bool Complex::equals(Complex other_complex) { + return re == other_complex.re && im == other_complex.im; +} diff --git a/solutions/2.1-class-types/complex.hpp b/solutions/2.1-class-types/complex.hpp new file mode 100644 index 0000000..25d7588 --- /dev/null +++ b/solutions/2.1-class-types/complex.hpp @@ -0,0 +1,35 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + // Default value is zero + Complex() = default; + // Construct purely real complex + Complex(double real); + // Construct from real and imaginary parts + Complex(double real, double imag); + + // Access components + double real(); + double imag(); + + // Compute the complex conjugate + Complex conj(); + + // Compute the magnitude + double norm(); + + // Add two complex numbers + Complex add(Complex other_complex); + + // Check if two complex numbers are equal + bool equals(Complex other_complex); + +private: + double re = 0.0; + double im = 0.0; +}; + +#endif diff --git a/solutions/2.1-class-types/test.cpp b/solutions/2.1-class-types/test.cpp new file mode 100644 index 0000000..d4e3e1f --- /dev/null +++ b/solutions/2.1-class-types/test.cpp @@ -0,0 +1,96 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + Complex z1{1, -83}; + Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm() == 0.0); + REQUIRE(Complex{3,4}.norm() == 5.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(z)); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + Complex expected{0.0, -y}; + Complex z_conj = z.conj(); + REQUIRE(z_conj.equals(expected)); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +TEST_CASE("Complex numbers can be compared") { + Complex zero; + Complex one{1.0}; + Complex i{0, 1}; + REQUIRE(zero.equals(zero)); + REQUIRE(one.equals(one)); + REQUIRE(i.equals(i)); + REQUIRE(!zero.equals(one)); + REQUIRE(!zero.equals(i)); + REQUIRE(!one.equals(i)); +} + +void CheckZplusZeq2Z(Complex z) { + Complex expected = Complex{2*z.real(), 2*z.imag()}; + Complex result = z.add(z); + REQUIRE(result.equals(expected)); +} +void CheckZminusZeq0(Complex z) { + Complex expected = Complex{}; + Complex z_minus = Complex{-z.real(), -z.imag()}; + Complex result = z.add(z_minus); + REQUIRE(result.equals(expected)); +} + +TEST_CASE("Complex number can be added") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + CheckZplusZeq2Z(Complex{1, 2}); + CheckZplusZeq2Z(Complex{-42, 1e-3}); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1, 2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} diff --git a/solutions/2.2-complex/Makefile b/solutions/2.2-complex/Makefile new file mode 100644 index 0000000..da851f6 --- /dev/null +++ b/solutions/2.2-complex/Makefile @@ -0,0 +1,15 @@ +CXXFLAGS = --std=c++17 -I../include + +test : complex.o test.o + $(CXX) $^ -o $@ + +test.o : catch.hpp + +catch.hpp : + wget https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/solutions/2.2-complex/README.md b/solutions/2.2-complex/README.md new file mode 100644 index 0000000..9fe8a98 --- /dev/null +++ b/solutions/2.2-complex/README.md @@ -0,0 +1,17 @@ +# Complex class exercise + +In your clone of this repository, find the `complex` exercise and list the files + +``` +$ cd archer2-cpp/exercises/complex$ ls Makefile complex.cpp complex.hpp test.cpp +``` + +The files `complex.hpp` and `complex.cpp` contain a partially working complex number class and `test.cpp` holds some basic unit tests. + +You can compile and run with: + +``` +$ make && ./testc++ --std=c++14 -I../include -c -o complex.o complex.cppc++ --std=c++14 -I../include -c -o test.o test.cppc++ complex.o test.o -o test===============================================================================All tests passed (34 assertions in 6 test cases) +``` + +But to get to this point you need to complete the code and fix a few bugs! diff --git a/solutions/2.2-complex/complex.cpp b/solutions/2.2-complex/complex.cpp new file mode 100644 index 0000000..b51c993 --- /dev/null +++ b/solutions/2.2-complex/complex.cpp @@ -0,0 +1,55 @@ +#include "complex.hpp" +#include + +Complex::Complex(double real) : re(real) { +} + +Complex::Complex(double real, double imag) : re(real), im(imag) { +} + +double Complex::real() const { + return re; +} + +double Complex::imag() const { + return im; +} + +Complex Complex::conj() const { + return Complex{re, -im}; +} + +double Complex::norm() const { + return std::sqrt(norm2()); +} + +double Complex::norm2() const { + return re*re + im*im; +} + +bool operator==(Complex const& a, Complex const& b) { + return (a.re == b.re) && (a.im == b.im); +} +bool operator!=(Complex const& a, Complex const& b) { + return !(a == b); +} + +Complex operator+(Complex const& a, Complex const& b) { + return Complex{a.re + b.re, a.im + b.im}; +} + +Complex operator-(Complex const& a, Complex const& b) { + return Complex{a.re - b.re, a.im - b.im}; +} + +Complex operator*(Complex const& a, Complex const& b) { + // (a + ib)*(c + id) == (a*c - b*d) + i(b*c + a*d) + return Complex{ + a.re*b.re - a.im*b.im, + a.re*b.im + a.im*b.re + }; +} + +Complex operator-(Complex const& a) { + return Complex{-a.re, -a.im}; +} diff --git a/solutions/2.2-complex/complex.hpp b/solutions/2.2-complex/complex.hpp new file mode 100644 index 0000000..d8593ab --- /dev/null +++ b/solutions/2.2-complex/complex.hpp @@ -0,0 +1,47 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP + +// Simple complex number class +class Complex { +public: + // Default value is zero + Complex() = default; + // Construct purely real complex + Complex(double real); + // Construct from real and imaginary parts + Complex(double real, double imag); + + // Access components + double real() const; + double imag() const; + + // Compute the complex conjugate + Complex conj() const; + + // Compute the magnitude and squared magnitude + double norm() const; + double norm2() const; + + // Declare comparisons + friend bool operator==(Complex const& a, Complex const& b); + friend bool operator!=(Complex const& a, Complex const& b); + + // Declare binary arithmetic operators + friend Complex operator+(Complex const& a, Complex const& b); + friend Complex operator-(Complex const& a, Complex const& b); + friend Complex operator*(Complex const& a, Complex const& b); + friend Complex operator/(Complex const& a, Complex const& b); + // Question: how would you declare multiplication and division by a real number? + friend Complex operator*(Complex const& a, double const& b); + friend Complex operator*(double const& a, Complex const& b); + + // Unary negation + friend Complex operator-(Complex const& a); + +private: + double re = 0.0; + double im = 0.0; +}; + + +#endif diff --git a/solutions/2.2-complex/test.cpp b/solutions/2.2-complex/test.cpp new file mode 100644 index 0000000..cb05e55 --- /dev/null +++ b/solutions/2.2-complex/test.cpp @@ -0,0 +1,98 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + const Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + const Complex one{1.0}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + const Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + const Complex z1{1, -83}; + const Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can be compared") { + const Complex zero; + const Complex one{1.0}; + const Complex i{0, 1}; + REQUIRE(zero == zero); + REQUIRE(one == one); + REQUIRE(i == i); + REQUIRE(zero != one); + REQUIRE(zero != i); + REQUIRE(one != i); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm2() == 0.0); + REQUIRE(Complex{3,4}.norm2() == 25.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + REQUIRE(z == z.conj()); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + + REQUIRE(z == -z.conj()); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +void CheckZplusZeq2Z(const Complex& z) { + REQUIRE(z + z == Complex{2*z.real(), 2*z.imag()}); +} +void CheckZminusZeq0(const Complex& z) { + REQUIRE(z - z == Complex{}); +} + +TEST_CASE("Complex number can be added and subtracted") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1,2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} + +TEST_CASE("Complex numbers can be multiplied") { + const Complex i{0, 1}; + Complex z{1}; + z = z*i; + REQUIRE(z == i); + z = z*i; + REQUIRE(z == Complex{-1}); + z = z*i; + REQUIRE(z == -i); +} diff --git a/solutions/3-containers/README.md b/solutions/3-containers/README.md new file mode 100644 index 0000000..ecee831 --- /dev/null +++ b/solutions/3-containers/README.md @@ -0,0 +1,24 @@ +# Containers exercise + +In your clone of this repository, find the `3-containers` exercise. It contains two sub-directories `part1` and `part2`. List +the files in `part1`: + +```bash +$ cd archer2-cpp/exercises/3-containers/part1 +$ ls +Makefile test.cpp vector_ex.cpp vector_ex.hpp +``` + +As before, `test.cpp` holds some basic unit tests and you can compile with `make`. + +## Part 1 +`vector_ex.cpp`/`.hpp` hold some functions that work on `std::vector` - provide the implementations + +## Part 2 +Copy `vector_ex.cpp`/`.hpp` from part 1 into the part 2 folder. + +Implement, in a new header/implementation pair of files (`map_ex.hpp`/`.cpp`), +a function (`AddWord`) that adds a string to a `std::map` as the key, the value +being the length of the string. + +You will want to find the documentatation for `map` on https://en.cppreference.com/ diff --git a/solutions/3-containers/part1/Makefile b/solutions/3-containers/part1/Makefile new file mode 100644 index 0000000..57ac7ba --- /dev/null +++ b/solutions/3-containers/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 -I../../include + +test : vector_ex.o test.o + $(CXX) $^ -o $@ + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/solutions/3-containers/part1/test.cpp b/solutions/3-containers/part1/test.cpp new file mode 100644 index 0000000..471c03d --- /dev/null +++ b/solutions/3-containers/part1/test.cpp @@ -0,0 +1,56 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "vector_ex.hpp" +#include + +// type alias +using IV = std::vector; + +IV IntRange(int n) { + auto ans = IV(n); + for (int i = 0; i < n; ++i) + ans[i] = i; + return ans; +} + +TEST_CASE("Vector Even") { + IV const empty; + REQUIRE(empty == GetEven(empty)); + + auto const zero = IntRange(1); + // Test the testing code! + REQUIRE(zero.size() == 1); + REQUIRE(zero[0] == 0); + + REQUIRE(zero == GetEven(zero)); + + auto const zero_one = IntRange(2); + // Test the testing code! + REQUIRE(zero_one.size() == 2); + REQUIRE(zero_one[0] == 0); + REQUIRE(zero_one[1] == 1); + + REQUIRE(zero == GetEven(zero_one)); + + // edited toddler random numbers + auto const random = IV{8, 127356, 1, 29, 4376, 263478, 1537}; + auto const random_even = IV{ 8, 127356, 4376, 263478}; + REQUIRE(GetEven(random) == random_even); +} + +std::string STR(IV const& v) { + std::stringstream ss; + PrintVectorOfInt(ss, v); + return ss.str(); +} + +TEST_CASE("Vector Print") { + REQUIRE(STR(IV{}) == "[ ]"); + REQUIRE(STR(IntRange(1)) == "[ 0]"); + REQUIRE(STR(IntRange(2)) == "[ 0, 1]"); + // edited toddler random numbers + REQUIRE(STR(IV{8, 127356, 1, 29, 4376, 263478, 1537}) == "[ 8, 127356, 1, 29, 4376, 263478, 1537]"); +} diff --git a/solutions/3-containers/part1/vector_ex.cpp b/solutions/3-containers/part1/vector_ex.cpp new file mode 100644 index 0000000..f7175db --- /dev/null +++ b/solutions/3-containers/part1/vector_ex.cpp @@ -0,0 +1,26 @@ +#include "vector_ex.hpp" + +// std::vector documentation: +// https://en.cppreference.com/w/cpp/container/vector + +std::vector GetEven(std::vector const& source) { + auto ans = std::vector{}; + for (auto&& elem: source) { + if (elem % 2 == 0) + ans.push_back(elem); + } + return ans; +} + +void PrintVectorOfInt(std::ostream& output, std::vector const& data) { + output << "[ "; + bool first = true; + for (auto const& x: data) { + if (!first) { + output << ", "; + } + first = false; + output << x; + } + output << "]"; +} diff --git a/solutions/3-containers/part1/vector_ex.hpp b/solutions/3-containers/part1/vector_ex.hpp new file mode 100644 index 0000000..22372c4 --- /dev/null +++ b/solutions/3-containers/part1/vector_ex.hpp @@ -0,0 +1,18 @@ +#ifndef CPPEX_VECTOR_EX_HPP +#define CPPEX_VECTOR_EX_HPP + +#include +#include + +// Here we declare two functions. +// Provide implementations, in vector_ex.cpp + +// Given a vector of integers, return a new vector with only the even +// elements from our input +std::vector GetEven(std::vector const& source); + +// Given a vector of ints, print the data to the stream +// [0, 1] +void PrintVectorOfInt(std::ostream& output, std::vector const& data); + +#endif diff --git a/solutions/3-containers/part2/Makefile b/solutions/3-containers/part2/Makefile new file mode 100644 index 0000000..c61fa37 --- /dev/null +++ b/solutions/3-containers/part2/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 -I../../include + +test : vector_ex.o map_ex.o test.o + $(CXX) $^ -o $@ + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/exercises/3-containers/part2/map_ex.cpp b/solutions/3-containers/part2/map_ex.cpp similarity index 100% rename from exercises/3-containers/part2/map_ex.cpp rename to solutions/3-containers/part2/map_ex.cpp diff --git a/exercises/3-containers/part2/map_ex.hpp b/solutions/3-containers/part2/map_ex.hpp similarity index 100% rename from exercises/3-containers/part2/map_ex.hpp rename to solutions/3-containers/part2/map_ex.hpp diff --git a/solutions/3-containers/part2/test.cpp b/solutions/3-containers/part2/test.cpp new file mode 100644 index 0000000..71d414b --- /dev/null +++ b/solutions/3-containers/part2/test.cpp @@ -0,0 +1,77 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "vector_ex.hpp" +#include "map_ex.hpp" +#include + +// type alias +using IV = std::vector; + +IV IntRange(int n) { + auto ans = IV(n); + for (int i = 0; i < n; ++i) + ans[i] = i; + return ans; +} + +TEST_CASE("Vector Even") { + IV const empty; + REQUIRE(empty == GetEven(empty)); + + auto const zero = IntRange(1); + // Test the testing code! + REQUIRE(zero.size() == 1); + REQUIRE(zero[0] == 0); + + REQUIRE(zero == GetEven(zero)); + + auto const zero_one = IntRange(2); + // Test the testing code! + REQUIRE(zero_one.size() == 2); + REQUIRE(zero_one[0] == 0); + REQUIRE(zero_one[1] == 1); + + REQUIRE(zero == GetEven(zero_one)); + + // edited toddler random numbers + auto const random = IV{8, 127356, 1, 29, 4376, 263478, 1537}; + auto const random_even = IV{ 8, 127356, 4376, 263478}; + REQUIRE(GetEven(random) == random_even); +} + +std::string STR(IV const& v) { + std::stringstream ss; + PrintVectorOfInt(ss, v); + return ss.str(); +} + +TEST_CASE("Vector Print") { + REQUIRE(STR(IV{}) == "[ ]"); + REQUIRE(STR(IntRange(1)) == "[ 0]"); + REQUIRE(STR(IntRange(2)) == "[ 0, 1]"); + // edited toddler random numbers + REQUIRE(STR(IV{8, 127356, 1, 29, 4376, 263478, 1537}) == "[ 8, 127356, 1, 29, 4376, 263478, 1537]"); +} + +using Word2Len = std::map; + +TEST_CASE("Map adding") { + Word2Len wlens; + + SECTION( "Adding a word works") { + bool did_insert = AddWord(wlens, "test"); + REQUIRE(did_insert); + REQUIRE(wlens.size() == 1); + REQUIRE(wlens.find("test") != wlens.end()); + + // Second time must return false + bool did_insert_second_time = AddWord(wlens, "test"); + REQUIRE(!did_insert_second_time); + REQUIRE(wlens.size() == 1); + REQUIRE(wlens.find("test") != wlens.end()); + } + +} diff --git a/solutions/3-containers/part2/vector_ex.cpp b/solutions/3-containers/part2/vector_ex.cpp new file mode 100644 index 0000000..f7175db --- /dev/null +++ b/solutions/3-containers/part2/vector_ex.cpp @@ -0,0 +1,26 @@ +#include "vector_ex.hpp" + +// std::vector documentation: +// https://en.cppreference.com/w/cpp/container/vector + +std::vector GetEven(std::vector const& source) { + auto ans = std::vector{}; + for (auto&& elem: source) { + if (elem % 2 == 0) + ans.push_back(elem); + } + return ans; +} + +void PrintVectorOfInt(std::ostream& output, std::vector const& data) { + output << "[ "; + bool first = true; + for (auto const& x: data) { + if (!first) { + output << ", "; + } + first = false; + output << x; + } + output << "]"; +} diff --git a/solutions/3-containers/part2/vector_ex.hpp b/solutions/3-containers/part2/vector_ex.hpp new file mode 100644 index 0000000..22372c4 --- /dev/null +++ b/solutions/3-containers/part2/vector_ex.hpp @@ -0,0 +1,18 @@ +#ifndef CPPEX_VECTOR_EX_HPP +#define CPPEX_VECTOR_EX_HPP + +#include +#include + +// Here we declare two functions. +// Provide implementations, in vector_ex.cpp + +// Given a vector of integers, return a new vector with only the even +// elements from our input +std::vector GetEven(std::vector const& source); + +// Given a vector of ints, print the data to the stream +// [0, 1] +void PrintVectorOfInt(std::ostream& output, std::vector const& data); + +#endif diff --git a/solutions/5-templates/README.md b/solutions/5-templates/README.md new file mode 100644 index 0000000..7bf6d85 --- /dev/null +++ b/solutions/5-templates/README.md @@ -0,0 +1,35 @@ +# Containers exercise + +In your clone of this repository, find the `5-templates` exercise. It contains two sub-directories `part1` and `part2`. + +## Part 1 + +List the files in `part1`: + +```bash +$ cd archer2-cpp/exercises/5-templates/part1 +$ ls +Makefile sum.cpp +``` + +1. Have a look at `sum.cpp`. Do you think it will compile? If so, what will the output be? +2. Compile and run the provided code with `make && ./sum`. Is the result what you expected? Can you explain what is happening at line 12? +```C++ +11 sum(3, 4); +12 sum(3.2, 5.1); +13 // sum("Hello", "World!"); +``` +3. Uncomment line 13. What happens when you try to compile? +4. Change the `sum()` function to use type templating. How does this change the output? Hint: C++ will not automatically convert from a char array to `std::string` so you will need to be explicit. + +## Part 2 + +List the files in `part2`: + +```bash +$ cd archer2-cpp/exercises/5-templates/part2 +$ ls +Makefile complex.cpp complex.hpp test.cpp +``` + +`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. diff --git a/solutions/5-templates/part1/Makefile b/solutions/5-templates/part1/Makefile new file mode 100644 index 0000000..26db992 --- /dev/null +++ b/solutions/5-templates/part1/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 -I../../include + +sum : sum.o + $(CXX) $^ -o $@ + +run : sum + ./sum + +clean : + rm -rf *.o sum diff --git a/solutions/5-templates/part1/sum.cpp b/solutions/5-templates/part1/sum.cpp new file mode 100644 index 0000000..5d56b99 --- /dev/null +++ b/solutions/5-templates/part1/sum.cpp @@ -0,0 +1,19 @@ +#include + +template +T sum(const T& a, const T& b) { + T result = a + b; + + std::cout << a << " + " << b << " = " << result << std::endl; + return result; +} + +int main() { + // 3 + 4 = 7 + sum(3, 4); + + // 3.2 + 5.1 = 8.3 + sum(3.2, 5.1); + + sum("Hello", "World!"); +} \ No newline at end of file diff --git a/solutions/5-templates/part2/Makefile b/solutions/5-templates/part2/Makefile new file mode 100644 index 0000000..d63bfbd --- /dev/null +++ b/solutions/5-templates/part2/Makefile @@ -0,0 +1,10 @@ +CXXFLAGS = --std=c++17 -I../../include + +test : test.o + $(CXX) $^ -o $@ + +run : test + ./test + +clean : + rm -rf *.o test diff --git a/solutions/5-templates/part2/complex.hpp b/solutions/5-templates/part2/complex.hpp new file mode 100644 index 0000000..5da1484 --- /dev/null +++ b/solutions/5-templates/part2/complex.hpp @@ -0,0 +1,109 @@ +#ifndef CPPEX_COMPLEX_COMPLEX_HPP +#define CPPEX_COMPLEX_COMPLEX_HPP +#include + +// Simple complex number class +template +class Complex { +public: + // Default value is zero + Complex() = default; + Complex(T real); + Complex(T real, T imag); + + // Access components + T real() const; + T imag() const; + + // Compute the complex conjugate + Complex conj() const; + + // Compute the magnitude and squared magnitude + double norm() const; + T norm2() const; + + // Declare comparisons + template + friend bool operator==(Complex const& a, Complex const& b); + template + friend bool operator!=(Complex const& a, Complex const& b); + + // Declare binary arithmetic operators + template + friend Complex> operator+(Complex const& a, Complex const& b); + template + friend Complex> operator-(Complex const& a, Complex const& b); + template + friend Complex> operator*(Complex const& a, Complex const& b); + + // Unary negation + template + friend Complex operator-(Complex const& a); + +private: + T re = 0.0; + T im = 0.0; +}; + +template +Complex::Complex(T real) : re(real) {} + +template +Complex::Complex(T real, T imag) : re(real), im(imag) {} + +template +T Complex::real() const { + return re; +} + +template +T Complex::imag() const { + return im; +} + +template +Complex Complex::conj() const { + return Complex{re, -im}; +} + +template +double Complex::norm() const { + return std::sqrt(norm2()); +} + +template +T Complex::norm2() const { + return re*re + im*im; +} + +template +bool operator==(Complex const& a, Complex const& b) { + return (a.re == b.re) && (a.im == b.im); +} + +template +bool operator!=(Complex const& a, Complex const& b) { + return !(a == b); +} + +template +Complex> operator+(Complex const& a, Complex const& b) { + return Complex>{a.re + b.re, a.im + b.im}; +} + +template +Complex> operator-(Complex const& a, Complex const& b) { + return Complex>{a.re - b.re, a.im - b.im}; +} + +template +Complex> operator*(Complex const& a, Complex const& b) { + return Complex>{a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re}; +} + +template +Complex operator-(Complex const& a) { + return Complex{-a.re, -a.im}; +} + +#endif diff --git a/solutions/5-templates/part2/test.cpp b/solutions/5-templates/part2/test.cpp new file mode 100644 index 0000000..265687e --- /dev/null +++ b/solutions/5-templates/part2/test.cpp @@ -0,0 +1,116 @@ +// Catch2 is a unit testing library +// Here we let it create a main() function for us +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "complex.hpp" + +TEST_CASE("Complex numbers are constructed real/imag parts readable") { + const Complex zero; + REQUIRE(zero.real() == 0.0); + REQUIRE(zero.imag() == 0.0); + + const Complex one{1}; + REQUIRE(one.real() == 1.0); + REQUIRE(one.imag() == 0.0); + + const Complex i{0, 1}; + REQUIRE(i.real() == 0.0); + REQUIRE(i.imag() == 1.0); + + const Complex z1{1, -83}; + const Complex z2 = z1; + REQUIRE(z1.real() == z2.real()); + REQUIRE(z1.imag() == z2.imag()); + REQUIRE(z2.real() == 1.0); + REQUIRE(z2.imag() == -83.0); +} + +TEST_CASE("Complex numbers can be compared") { + const Complex zero; + const Complex one{1}; + const Complex i{0, 1}; + REQUIRE(zero == zero); + REQUIRE(one == one); + REQUIRE(i == i); + REQUIRE(zero != one); + REQUIRE(zero != i); + REQUIRE(one != i); +} + +TEST_CASE("Complex numbers can have magnitude computed") { + REQUIRE(Complex{}.norm2() == 0.0); + REQUIRE(Complex{3,4}.norm2() == 25.0); +} + +// Pure real => z == z* +void CheckConjReal(double x) { + Complex z{x}; + REQUIRE(z == z.conj()); +} +// Pure imaginary => z* == -z +void CheckConjImag(double y) { + Complex z{0.0, y}; + + REQUIRE(z == -z.conj()); +} + +TEST_CASE("Complex numbers be conjugated") { + CheckConjReal(0); + CheckConjReal(1); + CheckConjReal(-3.14); + CheckConjReal(1.876e6); + + CheckConjImag(0); + CheckConjImag(1); + CheckConjImag(-3.14); + CheckConjImag(1.876e6); +} + +void CheckZplusZeq2Z(const Complex& z) { + REQUIRE(z + z == Complex{2*z.real(), 2*z.imag()}); +} +void CheckZminusZeq0(const Complex& z) { + REQUIRE(z - z == Complex{}); +} + +TEST_CASE("Complex number can be added and subtracted") { + CheckZplusZeq2Z(1); + CheckZplusZeq2Z(0); + CheckZplusZeq2Z(-1); + + CheckZminusZeq0(1); + CheckZminusZeq0(0); + CheckZminusZeq0(-1); + CheckZminusZeq0(Complex{1,2}); + CheckZminusZeq0(Complex{-42, 1e-3}); +} + +TEST_CASE("Complex numbers can be multiplied") { + const Complex i{0, 1}; + Complex z{1}; + z = z*i; + REQUIRE(z == i); + z = z*i; + REQUIRE(z == Complex{-1}); + z = z*i; + REQUIRE(z == -i); +} + +bool AreDoubleSame(double dFirstVal, double dSecondVal) +{ + return std::fabs(dFirstVal - dSecondVal) < 2 * std::numeric_limits::epsilon(); +} + +TEST_CASE("Complex numbers can be templated") { + const Complex z1{static_cast(3.4), static_cast(5.1)}; + const Complex z2{3.4, 5.1}; + REQUIRE(z1 != z2); + REQUIRE(z1.real() == 3); + REQUIRE(z1.imag() == 5); + REQUIRE(z1.norm2() == 34); + REQUIRE(z1 + z2 == Complex{6.4, 10.1}); + REQUIRE(std::is_same>::value); + REQUIRE(std::is_same>::value); + REQUIRE(std::is_same>::value); +} diff --git a/solutions/6.3-morton-order/Makefile b/solutions/6.3-morton-order/Makefile new file mode 100644 index 0000000..9d05269 --- /dev/null +++ b/solutions/6.3-morton-order/Makefile @@ -0,0 +1,7 @@ +include config.mk +exes = test_bits + +all : $(exes) + +clean : + -rm -f *.o $(exes) diff --git a/solutions/6.3-morton-order/README.md b/solutions/6.3-morton-order/README.md new file mode 100644 index 0000000..f8a502d --- /dev/null +++ b/solutions/6.3-morton-order/README.md @@ -0,0 +1,3 @@ +This document is available in multiple formats: +* [PDF](instructions.pdf) +* [Markdown](instructions.md) diff --git a/solutions/6.3-morton-order/bits.hpp b/solutions/6.3-morton-order/bits.hpp new file mode 100644 index 0000000..9382028 --- /dev/null +++ b/solutions/6.3-morton-order/bits.hpp @@ -0,0 +1,71 @@ +#ifndef MORTON_BITS_HPP +#define MORTON_BITS_HPP +#include + +namespace morton { + // Go from bit pattern like + // abcd + // to: + // 0a0b0c0d + inline uint64_t split(const uint32_t a) { + uint64_t x = a; + x = (x | x << 16) & 0x0000ffff0000ffffUL; + x = (x | x << 8) & 0x00ff00ff00ff00ffUL; + x = (x | x << 4) & 0x0f0f0f0f0f0f0f0fUL; + x = (x | x << 2) & 0x3333333333333333UL; + x = (x | x << 1) & 0x5555555555555555UL; + return x; + } + + // Reverse the above + inline uint32_t pack(const uint64_t z) { + uint64_t x = z; + x &= 0x5555555555555555UL; + x = x >> 1 | x; + x &= 0x3333333333333333UL; + x = x >> 2 | x; + x &= 0x0f0f0f0f0f0f0f0fUL; + x = x >> 4 | x; + x &= 0x00ff00ff00ff00ffUL; + x = x >> 8 | x; + x &= 0x0000ffff0000ffffUL; + x = x >> 16| x; + return x; + } + + // Compute the 2d Morton code for a pair of indices + inline uint64_t encode(const uint32_t x, const uint32_t y) { + return split(x) | split(y) << 1; + } + + // Compute the 2 indices from a Morton index + inline void decode(const uint64_t z, uint32_t& x, uint32_t& y) { + uint64_t i = z; + x = pack(i); + uint64_t j = z >> 1; + y = pack(j); + } + + const uint64_t odd_bit_mask = 0x5555555555555555UL; + const uint64_t even_bit_mask = 0xaaaaaaaaaaaaaaaaUL; + + // Move from (i, j) -> (i - 1, j) + inline uint64_t dec_x(const uint64_t z) { + return (((z & odd_bit_mask) - 1) & odd_bit_mask) | (z & even_bit_mask); + } + // Move from (i, j) -> (i + 1, j) + inline uint64_t inc_x(const uint64_t z) { + return (((z | even_bit_mask) + 1) & odd_bit_mask) | (z & even_bit_mask); + } + + // Move from (i, j) -> (i, j - 1) + inline uint64_t dec_y(const uint64_t z) { + return (z & odd_bit_mask) | (((z & even_bit_mask) - 1) & even_bit_mask); + } + // Move from (i, j) -> (i, j + 1) + inline uint64_t inc_y(const uint64_t z) { + return (z & odd_bit_mask) | (((z | odd_bit_mask) + 1) & even_bit_mask); + } + +} +#endif diff --git a/solutions/6.3-morton-order/config.mk b/solutions/6.3-morton-order/config.mk new file mode 100644 index 0000000..d7be2c8 --- /dev/null +++ b/solutions/6.3-morton-order/config.mk @@ -0,0 +1,2 @@ +CXXFLAGS = -g --std=c++11 -I.. +CC = $(CXX) diff --git a/solutions/6.3-morton-order/instructions.md b/solutions/6.3-morton-order/instructions.md new file mode 100644 index 0000000..07014ca --- /dev/null +++ b/solutions/6.3-morton-order/instructions.md @@ -0,0 +1,117 @@ +# Morton order matrices in C++ +## Rupert Nash +## r.nash@epcc.ed.ac.uk + +Source for this can be obtained from Github. Get a new copy with: + +``` +git clone https://github.com/EPCCed/archer2-CPP-2021-07-20 +``` + +or update your existing one with + +``` +git pull +``` + +then you can + +``` +cd archer2-CPP-2021-07-20/exercises/morton-order +``` + +The Morton ordering (or z-ordering) of a matrix lays out the elements +along a recursive z-shaped curve, as shown in the figure of four +iterations of the Z-order curve (from +[Wikipedia](https://en.wikipedia.org/wiki/Z-order_curve)). + +![Morton order](mortonorder.png) + +You can compute the Morton index `z` from the x- and y-indices (`i` +and `j` respectively) by interleaving their bits. An example is shown +in the table. + +| | 0 | 1 | 2 | 3 | +|----|----|----|----|----| +| 0 | 0| 1| 4| 5| +| 1 | 2| 3| 6| 7| +| 2 | 8| 9| 12| 13| +| 3 | 10| 11| 14| 15| + +Mapping between `x-y` indexes and Morton index for a 4 by 4 +matrix. Decimal on the left and binary on the right. + +| | 00 | 01 | 10 | 11 | +|----|------|------|------|-----| +| 00 | 0000 | 0001 | 0100 | 0101| +| 01 | 0010 | 0011 | 0110 | 0111| +| 10 | 1000 | 1001 | 1100 | 1101| +| 11 | 1010 | 1011 | 1110 | 1111| + +Mapping between `x-y` indexes and Morton index for a matrix of size +4-by-4. Decimal on the left and binary on the right. + +The advantage of laying out data in this way is that it improves data +locality (and hence cache use) without having to tune a block size or +similar parameter. On a modern multilevel cache machine[^1], this +means it can take good advantage of all the levels without tuning +multiple parameters. + +(E.g. an ARCHER node has L1, L2, and L3 caches, and the RAM is divided +into two NUMA regions. If using a PGAS approach one can view local RAM +as a cache for the distributed memory - i.e. 6 levels!) + +This exercise will walk you through a simple implementation. + +I have included implementations of the functions that do the +"bit-twiddling" for translating between a two-dimensional `x-y` index +and the Morton index, in the file `bits.hpp`. These are reasonably fast, +but can be beaten if you are interested to try! + +In what follows each section corresponds to a subdirectory with the same +number. + +## Implement the underlying data storage and element access + +Go to the step 1 directory: + +```bash +cd archer2-CPP-2021-07-20/exercises/morton-order/step1 +``` + +Using the partial implemenation in `matrix.hpp`, your task is to +implement the allocation (and release!) of memory to store the data and +to use the helper functions from `bits.hpp` to allow element access. You +will need to implement a number of member functions (marked in the +source with `\\ TODO`) and add whatever data members are needed (marked in +the same way). + +There is a test program `test_matrix_basic.cpp` which runs a few sanity +checks on your implementation (and similarly with `test_bits.cpp`). The +supplied `Makefile` should work. + +## Implement a basic iterator to traverse the matrix in order + +Go to the step 2 directory: + +```bash +cd archer2-CPP-2021-07-20/exercises/morton-order/step2 +``` + +I have a potential solution to part 1 here, but feel free to copy your +implementation into this. + +The exercise here is to complete the `matrix_iterator` class template +that I have started. I've provided most of the boilerplate to have +this work as a "bidirectional iterator". See +http://en.cppreference.com/w/cpp/concept/BidirectionalIterator for +full details of what this means, but basically it's one that can move +forward and backward through the data. + +Again, the things that need added are marked with `\\TODO`. The most +important thing to think about is how you will refer to the current +position and be able to traverse through it efficiently in Morton +order - the performance should be identical to looping over a raw +pointer! + + diff --git a/solutions/6.3-morton-order/instructions.pdf b/solutions/6.3-morton-order/instructions.pdf new file mode 100644 index 0000000..29cf50e Binary files /dev/null and b/solutions/6.3-morton-order/instructions.pdf differ diff --git a/solutions/6.3-morton-order/mortonorder.png b/solutions/6.3-morton-order/mortonorder.png new file mode 100644 index 0000000..b36cf29 Binary files /dev/null and b/solutions/6.3-morton-order/mortonorder.png differ diff --git a/solutions/6.3-morton-order/range.hpp b/solutions/6.3-morton-order/range.hpp new file mode 100644 index 0000000..edf43fb --- /dev/null +++ b/solutions/6.3-morton-order/range.hpp @@ -0,0 +1,133 @@ +/* range.hpp + * + * Copyright (c) 2013 Alexander Duchene + * + * This piece of software was created as part of the Drosophila Population + * Genomics Project opensource agreement. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RANGE_ITERATOR_HPP +#define RANGE_ITERATOR_HPP +#include + +namespace rangepp { + + template + class range_impl{ + private: + value_t rbegin; + value_t rend; + value_t step; + int step_end; + public: + range_impl(value_t begin, value_t end, value_t step=1): + rbegin(begin),rend(end),step(step){ + step_end=(rend-rbegin)/step; + if(rbegin+step_end*step != rend){ + step_end++; + } + } + + class iterator: + public std::iterator + { + private: + value_t current_value; + int current_step; + range_impl& parent; + public: + iterator(int start,range_impl& parent): current_step(start), parent(parent){current_value=parent.rbegin+current_step*parent.step;} + value_t operator*() {return current_value;} + const iterator* operator++(){ + current_value+=parent.step; + current_step++; + return this; + } + const iterator* operator++(int){ + current_value+=parent.step; + current_step++; + return this; + } + bool operator==(const iterator& other) { + return current_step==other.current_step; + } + bool operator!=(const iterator& other) { + return current_step!=other.current_step; + } + iterator operator+(int s) { + iterator ret=*this; + ret.current_step+=s; + ret.current_value+=s*parent.step; + return ret; + } + iterator operator-(int s){ + iterator ret=*this; + ret.current_step-=s; + ret.current_value-=s*parent.step; + return ret; + } + const iterator* operator--(){ + current_value-=parent.step; + current_step--; + return this;} + iterator operator--(int){ + iterator old=*this; + current_value-=parent.step; + current_step--; + return old; + } + }; + + iterator begin(){ + return iterator(0,*this); + } + iterator end(){ + return iterator(step_end,*this); + } + + value_t operator[](int s){ + return rbegin+s*step; + } + + int size(){ + return step_end; + } + }; +} +template +auto range(other begin, other end, vt stepsize)->rangepp::range_impl +{ + + return rangepp::range_impl(begin,end,stepsize); +} + +template +auto range(b begin, e end) -> rangepp::range_impl +{ + return rangepp::range_impl(begin,end,1); +} + +template +rangepp::range_impl range(e end){ + return rangepp::range_impl(0,end,1); +} + +#endif diff --git a/solutions/6.3-morton-order/step1/Makefile b/solutions/6.3-morton-order/step1/Makefile new file mode 100644 index 0000000..de2bded --- /dev/null +++ b/solutions/6.3-morton-order/step1/Makefile @@ -0,0 +1,11 @@ +include ../config.mk + +exes = test_matrix_base + +all : $(exes) + +test_matrix_base : test_matrix_base.cpp matrix.hpp + $(CXX) $(CXXFLAGS) $< -o $@ + +clean : + -rm -f *.o $(exes) diff --git a/solutions/6.3-morton-order/step1/matrix.hpp b/solutions/6.3-morton-order/step1/matrix.hpp new file mode 100644 index 0000000..ef94352 --- /dev/null +++ b/solutions/6.3-morton-order/step1/matrix.hpp @@ -0,0 +1,96 @@ +#ifndef MORTON_MATRIX_HPP +#define MORTON_MATRIX_HPP + +#include +#include +#include "bits.hpp" + +namespace morton { + + // 2D square matrix that stores data in Morton order + // + // NB: + // + // - This simple implementation requires that the size be a power + // of 2 (or zero indicating an empty matrix) + // + // - The matrix does not need to be resizeable + // + // - The matrix must not be implicitly copiable, must use explicit + // duplicate member function + template + class matrix { + public: + // Default initialisation of unique_ptr is OK + matrix() : _rank(0) { + } + + matrix(uint32_t r) + : _rank(r), _data(new T[r*r]) + { + // Check it's a power of 2. Could consider throwing an + // exception, but these are not in the syllabus! + assert((r & (r-1)) == 0); + } + + // Implicit copying is not allowed + matrix(const matrix& other) = delete; + matrix& operator=(const matrix& other) = delete; + + // Moving is allowed + // Default is ok because of choice to use unique_ptr to manage data storage + matrix(matrix&& other) noexcept = default; + matrix& operator=(matrix&& other) noexcept = default; + + // Destructor + // Default ok because of unique_ptr + ~matrix() = default; + + // Create a new matrix with contents copied from this one + matrix duplicate() const { + matrix ans(_rank); + std::copy(data(), data() + size(), ans.data.get()); + return ans; + } + + // Get rank size + uint32_t rank() const { + return _rank; + } + + // Get total size + uint64_t size() const { + return uint64_t(_rank) * uint64_t(_rank); + } + + // Const element access + const T& operator()(uint32_t i, uint32_t j) const { + auto z = encode(i, j); + return _data[z]; + } + + // Mutable element access + T& operator()(uint32_t i, uint32_t j) { + auto z = encode(i, j); + return _data[z]; + } + + // Raw data access (const and mutable versions) + const T* data() const { + return _data.get(); + } + T* data() { + return _data.get(); + } + + + private: + // rank of matrix + uint32_t _rank; + // Data storage + // Note using array version of unique_ptr + std::unique_ptr _data; + }; + +} +#endif diff --git a/solutions/6.3-morton-order/step1/test_matrix_base.cpp b/solutions/6.3-morton-order/step1/test_matrix_base.cpp new file mode 100644 index 0000000..0fd2b5d --- /dev/null +++ b/solutions/6.3-morton-order/step1/test_matrix_base.cpp @@ -0,0 +1,112 @@ +#include + +#include "matrix.hpp" +#include "test.hpp" +#include "range.hpp" + +bool test_small() { + const int N = 4; + morton::matrix small(N); + + // Fill with standard C array layout 1D index + for (auto i: range(N)) + for (auto j: range(N)) + small(i, j) = i*N + j; + + // Matrix contains: + // 0 4 8 12 + // 1 5 9 13 + // 2 6 10 14 + // 3 7 11 15 + + auto data = small.data(); + const std::vector expected = { + 0, 4, 1, 5, 8, 12, 9, 13, + 2, 6, 3, 7,10, 14,11, 15 + }; + for (auto z : range(N*N)) { + TEST_ASSERT_EQUAL(expected[z], data[z]); + } + return true; +} + +// Helper for making a matrix filled down the diagonal. +// This touches plenty of the pages to ensure mem is actually allocated. +morton::matrix make_diag(uint32_t rank) { + auto mat = morton::matrix(rank); + // fill diagonal + for (auto i: range(rank)) + mat(i,i) = i; + return mat; +} + +bool test_large() { + const int logN = 10; + const int N = 1 << logN; + auto mat = make_diag(N); + + auto data = mat.data(); + uint64_t z = 0; + // pretty easy to convince yourself that the "last" index in each + // successively bigger quad (starting at the origin) is (n^2 - 1) + // where n is the linear size of that. + + // So in the below we're talking about the values 0, 3, 15 + + // 0 1 4 5 + // 2 3 6 7 + // 8 9 12 13 + //10 11 14 15 + + + for (auto i: range(logN+1)) { + auto n = 1 << i; + auto z = n*n - 1; + TEST_ASSERT_EQUAL(n - 1, data[z]); + } + + return true; +} + +bool test_move() { + auto m1 = make_diag(4); + auto m2 = make_diag(8); + + m2 = std::move(m1); + // m1 is now moved-from: we can't do anything except destroy it or + // assign a new value + TEST_ASSERT_EQUAL(4, m2.rank()); + + // Test return of operator== + const morton::matrix& matref = (m1 = std::move(m2)); + // Also test the const element access version of operator() + for (auto i: range(4)) + for (auto j: range(4)) + TEST_ASSERT_EQUAL(matref(i,j), m1(i,j)); + + + return true; +} + +// Try to ensure we really are deleting used memory +bool test_free() { + const int logN = 10; + const int N = 1 << logN; + for (auto j: range(10000)) + auto mat = make_diag(N); + + return true; +} + + +int main() { + static_assert(!std::is_copy_constructible>::value, + "Require that morton matrix is not copyable"); + static_assert(std::is_move_constructible>::value, + "Require that morton matrix is moveable"); + RUN_TEST(test_small); + RUN_TEST(test_large); + RUN_TEST(test_move); + RUN_TEST(test_free); + return 0; +} diff --git a/solutions/6.3-morton-order/step2/Makefile b/solutions/6.3-morton-order/step2/Makefile new file mode 100644 index 0000000..af736b0 --- /dev/null +++ b/solutions/6.3-morton-order/step2/Makefile @@ -0,0 +1,13 @@ +include ../config.mk +exes = test_matrix_base test_matrix_iter + +all : $(exes) + +test_matrix_base : test_matrix_base.cpp matrix.hpp + $(CXX) $(CXXFLAGS) $< -o $@ + +test_matrix_iter : test_matrix_iter.cpp matrix.hpp + $(CXX) $(CXXFLAGS) $< -o $@ + +clean : + -rm -f *.o $(exes) diff --git a/solutions/6.3-morton-order/step2/matrix.hpp b/solutions/6.3-morton-order/step2/matrix.hpp new file mode 100644 index 0000000..d67715a --- /dev/null +++ b/solutions/6.3-morton-order/step2/matrix.hpp @@ -0,0 +1,190 @@ +#ifndef MORTON_MATRIX_HPP +#define MORTON_MATRIX_HPP + +#include +#include +#include +#include +#include "bits.hpp" + +namespace morton { + // Forward declare the iterator template + template class matrix_iterator; + + // 2D square matrix that stores data in Morton order + // + // NB: + // + // - This simple implementation requires that the size be a power + // of 2 (or zero indicating an empty matrix) + // + // - The matrix does not need to be resizeable + // + // - The matrix must not be implicitly copiable, must use explicit + // duplicate member function + template + class matrix { + public: + using iterator = matrix_iterator; + using const_iterator = matrix_iterator; + + matrix() : _rank(0) { + } + + matrix(uint32_t r) : _rank(r), _data(new T[r*r]) { + // Check it's a power of 2. Could consider throwing an + // exception, but these are not in the syllabus! + assert((r & (r-1)) == 0); + } + + // Implicit copying is not allowed + matrix(const matrix& other) = delete; + matrix& operator=(const matrix& other) = delete; + + // Moving is allowed + // Default is ok because of choice to use unique_ptr to manage data storage + matrix(matrix&& other) noexcept = default; + matrix& operator=(matrix&& other) noexcept = default; + + // Destructor + // Default ok because of unique_ptr + ~matrix() = default; + + // Create a new matrix with contents copied from this one + matrix duplicate() const { + matrix ans(_rank); + std::copy(begin(), end(), ans.begin()); + return ans; + } + + // Get rank size + uint32_t rank() const { + return _rank; + } + + // Get total size + uint64_t size() const { + return uint64_t(_rank) * uint64_t(_rank); + } + + // Const element access + const T& operator()(uint32_t i, uint32_t j) const { + auto z = encode(i, j); + return _data[z]; + } + + // Mutable element access + T& operator()(uint32_t i, uint32_t j) { + auto z = encode(i, j); + return _data[z]; + } + + // Raw data access (const and mutable versions) + const T* data() const { + return _data.get(); + } + T* data() { + return _data.get(); + } + + // Mutable iterators + iterator begin() { + return iterator(data(), data()); + } + iterator end() { + return iterator(data(), data() + size()); + } + + // Const iterators + const_iterator begin() const { + return const_iterator(data(), data()); + } + const_iterator end() const { + return const_iterator(data(), data() + size()); + } + + private: + // rank of matrix + uint32_t _rank; + // Data storage + // Note using array version of unique_ptr + std::unique_ptr _data; + }; + + // Note we inherit from std::iterator. + // This basically ensures our iterator has the right traits to work + // efficiently with the standard library. + // See http://en.cppreference.com/w/cpp/iterator/iterator + + // I've decided this should be a bidirectional iterator - i.e. you + // can move back and forwards. You only need to implement a handful + // of the methods, the rest follow from code I've done. + + // It could relatively easily be changed to a random access iterator + // by adding a few more operations - see: + // https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator + template + class matrix_iterator : + public std::iterator { + public: + // Default constructor + matrix_iterator() : _start(nullptr), _ptr(nullptr) { + } + + // Note: must provide copy c'tor, copy assign + // Defaults are fine + + // Get the x/y coordinates of the current element + uint32_t x() const { + auto z = _ptr - _start; + return pack(z); + } + uint32_t y() const { + auto z = _ptr - _start; + return pack(z >> 1); + } + + // Comparison operators. Note these are inline non-member friend + // functions. + friend bool operator==(const matrix_iterator& a, const matrix_iterator& b) { + return a._ptr == b._ptr; + } + // Note this can be done in terms of the above + friend bool operator!=(const matrix_iterator& a, const matrix_iterator& b) { + return !(a == b); + } + + // Dereference operator + T& operator*() { + return *_ptr; + } + + // Preincrement operator + matrix_iterator& operator++() { + ++_ptr; + return *this; + } + // Predecrement operator + matrix_iterator& operator--() { + --_ptr; + return *this; + } + + private: + matrix_iterator(T* start, T* current) : _start(start), _ptr(current) { + } + + // Other constructors should probably not be publicly visible, so + // we need to allow matrix access. + friend matrix::type>; + + + // We need the pointer to the first element to work out where we + // are in the matrix. + T* _start; + T* _ptr; + }; + +} +#endif diff --git a/solutions/6.3-morton-order/step2/test_matrix_base.cpp b/solutions/6.3-morton-order/step2/test_matrix_base.cpp new file mode 120000 index 0000000..df4b3fc --- /dev/null +++ b/solutions/6.3-morton-order/step2/test_matrix_base.cpp @@ -0,0 +1 @@ +../step1/test_matrix_base.cpp \ No newline at end of file diff --git a/solutions/6.3-morton-order/step2/test_matrix_iter.cpp b/solutions/6.3-morton-order/step2/test_matrix_iter.cpp new file mode 100644 index 0000000..9d66ddf --- /dev/null +++ b/solutions/6.3-morton-order/step2/test_matrix_iter.cpp @@ -0,0 +1,87 @@ +#include + +#include "matrix.hpp" +#include "test.hpp" +#include "range.hpp" + +morton::matrix make_filled(int N) { + morton::matrix mat(N); + + // Fill with standard C array layout 1D index + for (auto i: range(N)) + for (auto j: range(N)) + mat(i, j) = i*N + j; + return mat; +} + +// M = matrix or const matrix +template +bool check_mat(M& mat) { + // Matrix contains: + // 0 4 8 12 + // 1 5 9 13 + // 2 6 10 14 + // 3 7 11 15 + + // This will store the count of elements visited before the current + // and so should be the Morton index of the element. + int z = 0; + auto it = mat.begin(); + for (; it != mat.end(); ++it, ++z) { + uint32_t i, j; + morton::decode(z, i, j); + int expect = i*N + j; + TEST_ASSERT_EQUAL(expect, *it); + + TEST_ASSERT_EQUAL(i, it.x()); + TEST_ASSERT_EQUAL(j, it.y()); + } + TEST_ASSERT_EQUAL(N*N, z); + + return true; +} + +bool test_mut_iter() { + constexpr int N = 4; + auto mat = make_filled(N); + return check_mat(mat); +} + +// Do the same for the const iterator +bool test_const_iter() { + const int N = 4; + const morton::matrix& mat = make_filled(N); + return check_mat(mat); +} + +bool test_rev_iter() { + const int N = 4; + const morton::matrix& mat = make_filled(N); + + int z = N*N; + auto it = mat.end(); + for (; it != mat.begin();) { + // We want to run the below for it == mat.begin() and then finish + // so move decrement inside the body. + --it; + --z; + + uint32_t i, j; + morton::decode(z, i, j); + + int expect = i*N + j; + TEST_ASSERT_EQUAL(expect, *it); + + TEST_ASSERT_EQUAL(i, it.x()); + TEST_ASSERT_EQUAL(j, it.y()); + } + TEST_ASSERT_EQUAL(0, z); + return true; +} + +int main() { + RUN_TEST(test_mut_iter); + RUN_TEST(test_const_iter); + RUN_TEST(test_rev_iter); + return 0; +} diff --git a/solutions/6.3-morton-order/test.hpp b/solutions/6.3-morton-order/test.hpp new file mode 100644 index 0000000..9cc3bc1 --- /dev/null +++ b/solutions/6.3-morton-order/test.hpp @@ -0,0 +1,23 @@ +#ifndef MORTON_TEST_HPP +#define MORTON_TEST_HPP + +#include + +// A few macros to help with basic unit testing + +// (Don't want to introduce any dependencies by using a framework) + +#define TEST_ASSERT_EQUAL(expected, actual) \ + if (expected != actual) { \ + std::cerr << "FAIL! Expected '" << expected \ + << "' Got '" << actual << "'" << std::endl; \ + return false; \ + } + +// Runs a nullary predicate as a test +#define RUN_TEST(tfunc) if (tfunc()) \ + std::cerr << #tfunc << ": ok" << std::endl; \ + else \ + std::cerr << #tfunc << ": FAILED" << std::endl; + +#endif diff --git a/solutions/6.3-morton-order/test_bits.cpp b/solutions/6.3-morton-order/test_bits.cpp new file mode 100644 index 0000000..b8a4080 --- /dev/null +++ b/solutions/6.3-morton-order/test_bits.cpp @@ -0,0 +1,92 @@ +#include +#include "bits.hpp" +#include "test.hpp" + +using namespace morton; + +using p32_64 = std::pair; +const std::vector pdata = { + {0x00000000U, 0x0000000000000000UL}, + {0x00000001U, 0x0000000000000001UL}, + {0x00000002U, 0x0000000000000004UL}, + {0x00000004U, 0x0000000000000010UL}, + {0x00000008U, 0x0000000000000040UL}, + {0x00000010U, 0x0000000000000100UL}, + + {0x0000000fU, 0x0000000000000055UL}, + + {0xffffffffU, 0x5555555555555555UL}, +}; + + +bool test_split() { + for(auto& item: pdata) { + auto res = split(item.first); + // All odd bits must be zero + // 0xa == 0b1010 + auto mask = 0xaaaaaaaaaaaaaaaaUL; + if (mask & res) { + std::cerr << "FAIL! Have a non-zero odd bit in " << res << std::endl; + return false; + } + + TEST_ASSERT_EQUAL(item.second, res); + + } + return true; +} + + +bool test_pack() { + + for(auto& item: pdata) { + auto res = pack(item.second); + TEST_ASSERT_EQUAL(item.first, res); + } + return true; +} + + +const std::vector> enc_data = { + {0, 0, 0}, + {1, 0, 1}, + {0, 1, 2}, + {1, 1, 3}, + + {42, 7, 1134}, + {0x45812369U, 0xa7112504U, 0x983b42030c271461UL}, + {0xffffffffU, 0xffffffffU, 0xffffffffffffffffUL} +}; + +bool test_encode() { + for (auto& item: enc_data) { + auto& x = std::get<0>(item); + auto& y = std::get<1>(item); + auto& z = std::get<2>(item); + + auto res = encode(x, y); + TEST_ASSERT_EQUAL(z, res); + + uint32_t rx, ry; + decode(z, rx, ry); + + TEST_ASSERT_EQUAL(x, rx); + TEST_ASSERT_EQUAL(y, ry); + } + return true; +} + +bool test_shift() { + uint64_t start = 0; + auto res = dec_y(dec_x(inc_y(inc_x(start)))); + TEST_ASSERT_EQUAL(start, res); + return true; + +} +int main() { + RUN_TEST(test_split); + RUN_TEST(test_pack); + RUN_TEST(test_encode); + RUN_TEST(test_shift); + return 0; +} diff --git a/solutions/8-algorithm/Makefile b/solutions/8-algorithm/Makefile new file mode 100644 index 0000000..89717a5 --- /dev/null +++ b/solutions/8-algorithm/Makefile @@ -0,0 +1,4 @@ +CXXFLAGS = --std=c++17 +CC = $(CXX) + +ex : ex.o diff --git a/solutions/8-algorithm/README.md b/solutions/8-algorithm/README.md new file mode 100644 index 0000000..278cb79 --- /dev/null +++ b/solutions/8-algorithm/README.md @@ -0,0 +1,12 @@ +# Algorithm use exercise + +In the file `ex.cpp` there is an incomplete program which, by +following the instructions in the comments, you can finish. + +You will likely want to refer to the documentation of the standard +library algorithms, which, for reasons, are split across two headers: + +- + +- + diff --git a/solutions/8-algorithm/ex.cpp b/solutions/8-algorithm/ex.cpp new file mode 100644 index 0000000..e4f5ba2 --- /dev/null +++ b/solutions/8-algorithm/ex.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +void print_vec(std::vector &vec) { + for (auto &el : vec) { + std::cout << el << " "; + } + std::cout << std::endl << std::endl; +} + +int main(int argc, char* argv[]) { + // First, a warmup of basic algorithm usage + auto nums = std::vector(50); + + // let's initalize our vector with some random numbers + for (int i = 0; i < 50; ++i) { + nums[i] = std::rand() % 100; + } + + // Bonus: can we do this using the algorithms library? Hint - std::generate + // and use the following lambda + auto gen = []() { return std::rand() % 100; }; + + // Your code here.... + std::generate(begin(nums), end(nums), gen); + + // Now, sort nums. + + // Your code here.... + std::sort(nums.begin(), nums.end()); + + std::cout << "Sorted nums: "; + print_vec(nums); + // Reverse sort nums, using (a) sort on its own and (b) using sort and another + // algorithm function + + // Your code here.... + std::sort(nums.begin(), nums.end(), std::greater()); + + std::cout << "Reverse sorted nums (a): "; + print_vec(nums); + + // Your code here.... + std::sort(nums.begin(), nums.end()); + std::reverse(nums.begin(), nums.end()); + + std::cout << "Reverse sorted nums (b): "; + print_vec(nums); + + // Now, lets look at a more involved example. We'll be working through Project + // Euler No.2 (https://projecteuler.net/problem=2) "By considering the terms in + // the Fibonacci sequence whose values do not exceed four million, find the sum + // of the even-valued terms" + + // First lets get the fist 47 fibonacci numbers + // BONUS: use std::transform + + auto fibs = std::vector(47); + + // Your code here.... + fibs[0] = 0; + fibs[1] = 1; + + std::transform(fibs.begin(), fibs.end() - 2, fibs.begin() + 1, + fibs.begin() + 2, std::plus()); + + print_vec(fibs); + + // Next, get all that are less than or equal to 4 million, and store them in + // fibs_less HINT: use std::copy_if and std::back_inserter + + auto fibs_less = std::vector(); + + // Your code here.... + std::copy_if(fibs.begin(), fibs.end(), std::back_inserter(fibs_less), + [](int x) { return x <= 4000000; }); + + std::cout << "fibs <= 4000000: "; + print_vec(fibs_less); + + // Now, get the evens. Use the same approach as above + auto evens = std::vector(); + + // Your code here.... + + std::copy_if(fibs_less.begin(), fibs_less.end(), std::back_inserter(evens), + [](int x) { return !(x % 2); }); + + std::cout << "Evens: "; + print_vec(evens); + + // Finally, let's sum them (hint: std::accumulate) + + int sum = std::accumulate(evens.begin(), evens.end(), 0); + + std::cout << "Sum of even fibonacci numbers not greater than 4 million: " + << sum << std::endl; + + return 0; +} diff --git a/solutions/9-eigen/Makefile b/solutions/9-eigen/Makefile new file mode 100644 index 0000000..10bc750 --- /dev/null +++ b/solutions/9-eigen/Makefile @@ -0,0 +1,15 @@ +CXX=clang++ +CXX=icpc +CXX=g++ +CPPFLAGS=-O2 -std=c++11 + +all: explicit implicit sparse + +explicit: explicit.o + $(CXX) $(CPPFLAGS) -o $@ $< + +implicit: implicit.o + $(CXX) $(CPPFLAGS) -o $@ $< + +sparse: sparse.o + $(CXX) $(CPPFLAGS) -o $@ $< diff --git a/solutions/9-eigen/README.md b/solutions/9-eigen/README.md new file mode 100644 index 0000000..2159542 --- /dev/null +++ b/solutions/9-eigen/README.md @@ -0,0 +1,6 @@ +# Eigen Demos + +Some simple demos using the Eigen C++ library to solve 1D diffusion +problems. + +See Eigen lecture for details diff --git a/solutions/9-eigen/explicit.cpp b/solutions/9-eigen/explicit.cpp new file mode 100644 index 0000000..3e0003e --- /dev/null +++ b/solutions/9-eigen/explicit.cpp @@ -0,0 +1,48 @@ +#include + +#include +#include +#include + +int main() +{ + int n = 20; + int steps = 200; + std::vector Avec(n * n); + + + // Set up matrix A + Eigen::Map A(Avec.data(), n, n); + A = Eigen::MatrixXd::Identity(n, n); + double delta = 0.4; + for (int i = 0; i < n - 1; ++i) + { + A(i + 1, i) += delta; + A(i + 1, i + 1) += -delta; + + A(i, i) += -delta; + A(i, i + 1) += +delta; + } + + std::cout << "A = \n" << A << std::endl + << std::endl; + + + // T_n + Eigen::VectorXd b(n); + b.setZero(); + b.head(n / 2).array() = 1.0; + + std::ofstream f; + f.open("explicit_sim.txt"); + for (int i = 0; i < steps; ++i) + { + f << b.transpose() << std::endl; + // update time-step T_{n+1} = A.T_{n} + b = A * b; + } + + std::cout << b.transpose() << std::endl; + + return 0; +} \ No newline at end of file diff --git a/solutions/9-eigen/implicit.cpp b/solutions/9-eigen/implicit.cpp new file mode 100644 index 0000000..e2b0a39 --- /dev/null +++ b/solutions/9-eigen/implicit.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include + +int main() +{ + int n = 20; + int steps = 100; + + // Set up matrix A + Eigen::MatrixXd A(n, n); + A = Eigen::MatrixXd::Identity(n, n); + + double delta = -0.4; + + for (int i = 0; i < n - 1; ++i) + { + A(i + 1, i) += delta; + A(i + 1, i + 1) += -delta; + + A(i, i) += -delta; + A(i, i + 1) += +delta; + } + + std::cout << "A = \n" + << A << std::endl + << std::endl; + + Eigen::VectorXd b(n); + b.setZero(); + + b.head(n / 2).array() = 1.0; + + std::ofstream f; + f.open("implicit_sim.txt"); + for (int i = 0; i < 200; ++i) + { + // Solve A.T_{n+1} = T_{n} for T_{n+1} + f << b.transpose() << std::endl; + b = A.colPivHouseholderQr().solve(b); + } + + std::cout << b.transpose() << std::endl; + return 0; +} \ No newline at end of file diff --git a/solutions/9-eigen/modules.sh b/solutions/9-eigen/modules.sh new file mode 100644 index 0000000..29f2a84 --- /dev/null +++ b/solutions/9-eigen/modules.sh @@ -0,0 +1,5 @@ +#!/bin/bash +module load PrgEnv-gnu +module load cray-python +module load matplotlib +module load eigen/3.4.0 diff --git a/solutions/9-eigen/movie.py b/solutions/9-eigen/movie.py new file mode 100644 index 0000000..b419109 --- /dev/null +++ b/solutions/9-eigen/movie.py @@ -0,0 +1,39 @@ +import argparse +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.animation import FuncAnimation + +def plot(simulation): + n = 20 + steps = 200 + + fig, ax = plt.subplots() + xdata = np.arange(0, n) + ydata = [] + ln, = ax.plot([], [], 'ro') + + data = np.loadtxt(f"{simulation}_sim.txt") + + def init(): + ax.set_xlim(0,n) + ax.set_ylim(0,1.1) + return ln, + + def update(frame): + ydata = data[frame] + ln.set_data(xdata, ydata) + return ln, + + ani = FuncAnimation(fig, update, frames=np.arange(0, 200), + init_func=init, blit=True) + + ani.save(f"{simulation}.gif", writer="pillow") + plt.show() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(prog='movie.py', description='Create animation from simulation output.') + parser.add_argument('simulation', default='implicit', const='implicit', nargs='?', choices=['implicit', 'explicit', 'sparse']) + + args = parser.parse_args() + plot(args.simulation) diff --git a/solutions/9-eigen/sparse.cpp b/solutions/9-eigen/sparse.cpp new file mode 100644 index 0000000..da6f7d9 --- /dev/null +++ b/solutions/9-eigen/sparse.cpp @@ -0,0 +1,50 @@ +#include +#include + +#include +#include +#include + +int main() +{ + int n = 20; + int steps = 200; + + Eigen::SparseMatrix A; + A.resize(n, n); + + double delta = 0.4; + + std::vector> fill; + fill.reserve(n * 4); + + for (int i = 0; i < n - 1; ++i) + { + fill.push_back(Eigen::Triplet(i + 1, i, delta)); + fill.push_back(Eigen::Triplet(i + 1, i + 1, -delta)); + fill.push_back(Eigen::Triplet(i, i, 1.0 - delta)); + fill.push_back(Eigen::Triplet(i, i + 1, delta)); + } + fill.push_back(Eigen::Triplet(n - 1, n - 1, 1.0)); + A.setFromTriplets(fill.begin(), fill.end()); + + std::cout << A << std::endl; + + Eigen::VectorXd b(n); + b.head(n / 2).array() = 1.0; + b.tail(n / 2).array() = 0.0; + + std::cout << b.transpose() << std::endl; + + std::ofstream f; + f.open("sparse_sim.txt"); + for (int i = 0; i < steps; ++i) + { + f << b.transpose() << std::endl; + b = A * b; + } + + std::cout << b.transpose() << std::endl; + + return 0; +} \ No newline at end of file diff --git a/solutions/README.md b/solutions/README.md new file mode 100644 index 0000000..ef3b791 --- /dev/null +++ b/solutions/README.md @@ -0,0 +1,18 @@ +# C++ exercises + +Solutions to the C++ course practical exercises. + +See each subdirectory for further instructions: + +* [2.1 Class types](2.1-class-types/) +* [2.2 Complex numbers](2.2-complex/) +* [3 Containers](3-containers/) +* [4 Pointers](4-pointers/) +* [5 Templates](5-templates/) +* [6.1 My array](6.1-my-array/) +* [6.2 Special pointers](6.2-special-pointers/) +* [6.3 Morton-order matrix class template](6.3-morton-order/) +* [7 Inheritance](7-inheritance/) +* [8 Using algorithms](8-algorithm/) +* [9 Eigen](9-eigen/) +* [10 Simple use of threads](10-threads/) diff --git a/solutions/include/catch.hpp b/solutions/include/catch.hpp new file mode 100644 index 0000000..36eaeb2 --- /dev/null +++ b/solutions/include/catch.hpp @@ -0,0 +1,17937 @@ +/* + * Catch v2.13.6 + * Generated: 2021-04-16 18:23:38.044268 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 6 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template