{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to C++" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hello world\n", "\n", "There are many lessons in writing a simple \"Hello world\" program\n", "\n", "- C++ programs are normally written using a text editor or integrated development environment (IDE) - we use the %%file magic to simulate this\n", "- The #include statement literally pulls in and prepends the source code from the `iostream` header file\n", "- Types must be declared - note the function return type is `int`\n", "- There is a single function called `main` - every program has `main` as the entry point although you can write libraries without a `main` function\n", "- Notice the use of braces to delimit blocks\n", "- Notice the use of semi-colons to delimit expressions\n", "- Unlike Python, white space is not used to delimit blocks or expressions only tokens\n", "- Note the use of the `std` *namespace* - this is similar to Python except C++ uses `::` rather than `.` (like R)\n", "- The I/O shown here uses *streaming* via the `<<` operator to send output to `cout`, which is the name for the standard output\n", "- `std::endl` provides a line break and flushes the input buffer" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing hello.cpp\n" ] } ], "source": [ "%%file hello.cpp\n", "\n", "#include \n", "\n", "int main() {\n", " std::cout << \"Hello, world!\" << std::endl;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compilation\n", "\n", "- The source file must be compiled to machine code before it can be exeuted\n", "- Compilation is done with a C++ compiler - here we use one called `g++`\n", "- By default, the output of compilation is called `a.out` - we use `-o` to change the output executable filename to `hello.exe`\n", " - Note the use of `.exe` is a Windows convention; Unix executables typically have no extension - for example, just be the name `hello`" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ hello.cpp -o hello.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Execution" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, world!\n" ] } ], "source": [ "%%bash\n", "\n", "./hello.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### C equivalent\n", "\n", "Before we move on, we briefly show the similar `Hello world` program in C. C is a precursor to C++ that is still widely used. While C++ is derived from C, it is a much richer and more complex language. We focus on C++ because the intent is to show how to wrap C++ code using `pybind11` and take advantage of C++ numerical libraries that do not exist in C." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing hello01.c\n" ] } ], "source": [ "%%file hello01.c\n", "\n", "#include \n", "\n", "int main() {\n", " printf(\"Hello, world from C!\\n\");\n", "}" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "gcc hello01.c" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, world from C!\n" ] } ], "source": [ "%%bash\n", "\n", "./a.out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Namespaces\n", "\n", "Just like Python, C++ has namespaces that allow us to build large libraries without worrying about name collisions. In the `Hello world` program, we used the explicit name `std::cout` indicating that `cout` is a member of the standard workspace. We can also use the `using` keyword to import selected functions or classes from a namespace. \n", "\n", "```c++\n", "using std::cout;\n", "\n", "int main()\n", "{\n", " cout << \"Hello, world!\\n\";\n", "}\n", "```\n", "\n", "For small programs, we sometimes import the entire namespace for convenience, but this may cause namespace collisions in larger programs.\n", "\n", "```c++\n", "using namespace std;\n", "\n", "int main()\n", "{\n", " cout << \"Hello, world!\\n\";\n", "}\n", "```\n", "\n", "You can easily create your own namespace.\n", "\n", "```c++\n", "namespace sta_663 {\n", " const double pi=2.14159;\n", "\n", " void greet(string name) {\n", " cout << \"\\nTraditional first program\\n\";\n", " cout << \"Hello, \" << name << \"\\n\";\n", " }\n", "}\n", "\n", "int main() \n", "{\n", " cout << \"\\nUsing namespaces\\n\";\n", " string name = \"Tom\";\n", " cout << sta_663::pi << \"\\n\";\n", " sta_663::greet(name);\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Using qualified imports" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing hello02.cpp\n" ] } ], "source": [ "%%file hello02.cpp\n", "\n", "#include \n", "\n", "using std::cout;\n", "using std::endl;\n", "\n", "int main() {\n", " cout << \"Hello, world!\" << endl;\n", "}" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ hello02.cpp -o hello02" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, world!\n" ] } ], "source": [ "%%bash\n", "\n", "./hello02" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Global imports of a namespace\n", "\n", "Wholesale imports of namespace is generally frowned upon, similar to how `from X import *` is frowned upon in Python." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing hello03.cpp\n" ] } ], "source": [ "%%file hello03.cpp\n", "\n", "#include \n", "\n", "using namespace std;\n", "\n", "int main() {\n", " cout << \"Hello, world!\" << endl;\n", "}" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ hello03.cpp -o hello03" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, world!\n" ] } ], "source": [ "%%bash\n", "\n", "./hello03" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Types" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing dtypes.cpp\n" ] } ], "source": [ "%%file dtypes.cpp\n", "\n", "#include \n", "#include \n", "\n", "using std::cout;\n", "\n", "int main() {\n", " // Boolean\n", " bool a = true, b = false; \n", " \n", " cout << \"and \" << (a and b) << \"\\n\";\n", " cout << \"&& \" << (a && b) << \"\\n\";\n", " cout << \"or \" << (a or b) << \"\\n\";\n", " cout << \"|| \" << (a || b) << \"\\n\";\n", " cout << \"not \" << not (a or b) << \"\\n\";\n", " cout << \"! \" << !(a or b) << \"\\n\";\n", "\n", " // Integral numbers\n", " cout << \"char \" << sizeof(char) << \"\\n\";\n", " cout << \"short int \" << sizeof(short int) << \"\\n\";\n", " cout << \"int \" << sizeof(int) << \"\\n\";\n", " cout << \"long \" << sizeof(long) << \"\\n\";\n", "\n", " // Floating point numbers\n", " cout << \"float \" << sizeof(float) << \"\\n\";\n", " cout << \"double \" << sizeof(double) << \"\\n\";\n", " cout << \"long double \" << sizeof(long double) << \"\\n\";\n", " cout << \"complex double \" << sizeof(std::complex) << \"\\n\";\n", " \n", " // Characters and strings\n", " char c = 'a'; // Note single quotes\n", " char word[] = \"hello\"; // C char arrays\n", " std::string s = \"hello\"; // C++ string\n", " \n", " cout << c << \"\\n\";\n", " cout << word << \"\\n\";\n", " cout << s << \"\\n\"; \n", "}" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "and 0\n", "&& 0\n", "or 1\n", "|| 1\n", "not 0\n", "! 0\n", "char 1\n", "short int 2\n", "int 4\n", "long 8\n", "float 4\n", "double 8\n", "long double 16\n", "complex double 16\n", "a\n", "hello\n", "hello\n" ] } ], "source": [ "%%bash\n", "\n", "g++ dtypes.cpp -o dtypes.exe\n", "./dtypes.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Type conversions\n", "\n", "Converting between types can get pretty complicated in C++. We will show some simple versions." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing type.cpp\n" ] } ], "source": [ "%%file type.cpp\n", "#include \n", "using std::cout;\n", "using std::string;\n", "using std::stoi;\n", " \n", "int main() {\n", " char c = '3'; // A char is an integer type\n", " string s = \"3\"; // A string is not an integer type\n", " int i = 3;\n", " float f = 3.1;\n", " double d = 3.2;\n", " \n", " cout << c << \"\\n\";\n", " cout << i << \"\\n\";\n", " cout << f << \"\\n\";\n", " cout << d << \"\\n\";\n", " \n", " cout << \"c + i is \" << c + i << \"\\n\";\n", " cout << \"c + i is \" << c - '0' + i << \"\\n\";\n", " \n", " // Casting string to number\n", " cout << \"s + i is \" << stoi(s) + i << \"\\n\"; // Use std::stod to convert to double\n", " \n", " // Two ways to cast float to int\n", " cout << \"f + i is \" << f + i << \"\\n\"; \n", " cout << \"f + i is \" << int(f) + i << \"\\n\"; \n", " cout << \"f + i is \" << static_cast(f) + i << \"\\n\"; \n", "\n", "}\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o type.exe type.cpp -std=c++14" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "3\n", "3.1\n", "3.2\n", "c + i is 54\n", "c + i is 6\n", "s + i is 6\n", "f + i is 6.1\n", "f + i is 6\n", "f + i is 6\n" ] } ], "source": [ "%%bash\n", "\n", "./type.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Header, source, and driver files\n", "\n", "C++ allows separate compilation of functions and programs that use those functions. The way it does this is to write functions in *source* files that can be compiled. To use these compiled functions, the calling program includes *header* files that contain the function signatures - this provides enough information for the compiler to link to the compiled function machine code when executing the program.\n", "\n", "- Here we show a toy example of typical C++ program organization\n", "- We build a library of math functions in `my_math.cpp`\n", "- We add a header file for the math functions in `my_math.hpp`\n", "- We build a library of stats functions in `my_stats.cpp`\n", "- We add a header file for the stats functions in `my_stats.hpp`\n", "- We write a program that uses math and stats functions called `my_driver.cpp`\n", " - We pull in the function signatures with `#include` for the header files\n", "- Once you understand the code, move on to see how compilation is done\n", "- Note that it is customary to include the header file in the source file itself to let the compiler catch any mistakes in the function signatures" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing my_math.hpp\n" ] } ], "source": [ "%%file my_math.hpp\n", "#pragma once\n", "\n", "int add(int a, int b);\n", "int multiply(int a, int b);" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing my_math.cpp\n" ] } ], "source": [ "%%file my_math.cpp\n", "\n", "#include \"my_math.hpp\"\n", "\n", "int add(int a, int b) {\n", " return a + b;\n", "}\n", "\n", "int multiply(int a, int b) {\n", " return a * b;\n", "}" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing my_stats.hpp\n" ] } ], "source": [ "%%file my_stats.hpp\n", "#pragma once\n", "\n", "int mean(int xs[], int n);" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing my_stats.cpp\n" ] } ], "source": [ "%%file my_stats.cpp\n", "\n", "#include \"my_math.hpp\"\n", "\n", "int mean(int xs[], int n) {\n", " double s = 0;\n", " for (int i=0; i\n", "#include \"my_math.hpp\"\n", "#include \"my_stats.hpp\"\n", "\n", "int main() {\n", " int xs[] = {1,2,3,4,5};\n", " int n = 5;\n", " int a = 3, b= 4;\n", " \n", " std::cout << \"sum = \" << add(a, b) << \"\\n\";\n", " std::cout << \"prod = \" << multiply(a, b) << \"\\n\";\n", " std::cout << \"mean = \" << mean(xs, n) << \"\\n\";\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compilation\n", "\n", "- Notice in the first 2 compile statements, that the source files are compiled to *object* files with default extension `.o` by usin gthe flag `-c`\n", "- The 3rd compile statement builds an *executable* by linking the `main` file with the recently created object files\n", "- The function signatures in the included header files tells the compiler how to match the function calls `add`, `multiply` and `mean` with the matching compiled functions" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -c my_math.cpp\n", "g++ -c my_stats.cpp\n", "g++ my_driver.cpp my_math.o my_stats.o" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sum = 7\n", "prod = 12\n", "mean = 3\n" ] } ], "source": [ "%%bash\n", "\n", "./a.out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using `make`\n", "\n", "As building C++ programs can quickly become quite complicated, there are *builder* programs that help simplify this task. One of the most widely used is `make`, which uses a file normally called `Makefile` to coordinate the instructions for building a program\n", "\n", "- Note that `make` can be used for more than compiling programs; for example, you can use it to automatically rebuild tables and figures for a manuscript whenever the data is changed\n", "- Another advantage of `make` is that it keeps track of dependencies, and only re-compiles files that have changed or depend on another changed file since the last compilation\n", "\n", "We will build a simple `Makefile` to build the `my_driver` executable:\n", "\n", "- Each section consists of a make target denoted by `:` followed by files the target depends on\n", "- The next line is the command given to build the target. This must begin with a TAB character (it MUST be a TAB and not spaces)\n", "- If a target has dependencies that are not met, `make` will see if each dependency itself is a target and build that first\n", "- It uses timestamps to decide whether to rebuild a target (not actually changes)\n", "- By default, `make` builds the first target, but can also build named targets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How to get the TAB character. Copy and paste the blank space between `a` and `b`." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a\tb\r\n" ] } ], "source": [ "! echo \"a\\tb\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\t" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing Makefile\n" ] } ], "source": [ "%%file Makefile\n", "\n", "driver: my_math.o my_stats.o\n", "\tg++ my_driver.cpp my_math.o my_stats.o -o my_driver\n", " \n", "my_math.o: my_math.cpp my_math.hpp\n", "\tg++ -c my_math.cpp\n", " \n", "my_stats.o: my_stats.cpp my_stats.hpp\n", "\tg++ -c my_stats.cpp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- We first start with a clean slate" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "%%capture logfile\n", "%%bash\n", "\n", "rm *\\.o\n", "rm my_driver" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "g++ -c my_math.cpp\n", "g++ -c my_stats.cpp\n", "g++ my_driver.cpp my_math.o my_stats.o -o my_driver\n" ] } ], "source": [ "%%bash\n", "\n", "make" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sum = 7\n", "prod = 12\n", "mean = 3\n" ] } ], "source": [ "%%bash\n", "\n", "./my_driver" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Re-building does not trigger re-compilation of source files since the timestamps have not changed" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "g++ my_driver.cpp my_math.o my_stats.o -o my_driver\n" ] } ], "source": [ "%%bash\n", "\n", "make" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "touch my_stats.hpp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- As `my_stats.hpp` was listed as a dependency of the target `my_stats.o`, `touch`, which updates the timestamp, forces a recompilation of `my_stats.o`" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "g++ -c my_stats.cpp\n", "g++ my_driver.cpp my_math.o my_stats.o -o my_driver\n" ] } ], "source": [ "%%bash\n", "\n", "make" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Use of variables in Makefile" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing Makefile2\n" ] } ], "source": [ "%%file Makefile2\n", "\n", "CC=g++\n", "CFLAGS=-Wall -std=c++14\n", " \n", "driver: my_math.o my_stats.o\n", "\t$(CC) $(CFLAGS) my_driver.cpp my_math.o my_stats.o -o my_driver2\n", " \n", "my_math.o: my_math.cpp my_math.hpp\n", "\t$(CC) $(CFLAGS) -c my_math.cpp\n", " \n", "my_stats.o: my_stats.cpp my_stats.hpp\n", "\t$(CC) $(CFLAGS) -c my_stats.cpp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compilation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that no re-compilation occurs!" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "g++ -Wall -std=c++14 my_driver.cpp my_math.o my_stats.o -o my_driver2\n" ] } ], "source": [ "%%bash\n", "\n", "make -f Makefile2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Execution" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sum = 7\n", "prod = 12\n", "mean = 3\n" ] } ], "source": [ "%%bash \n", "\n", "./my_driver2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Input and output" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Arguments to main" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing main_args.cpp\n" ] } ], "source": [ "%%file main_args.cpp\n", "\n", "#include \n", "using std::cout;\n", " \n", "int main(int argc, char* argv[]) {\n", " for (int i=0; i\n", "#include \"my_math.hpp\"\n", "\n", "int main() {\n", " std::ifstream fin(\"data.txt\");\n", " std::ofstream fout(\"result.txt\");\n", " \n", " double a, b;\n", " \n", " fin >> a >> b;\n", " fin.close();\n", " \n", " fout << add(a, b) << std::endl;\n", " fout << multiply(a, b) << std::endl;\n", " fout.close();\n", "}" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ io.cpp -o io.exe my_math.cpp " ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "./io.exe" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "15\r\n", "54\r\n" ] } ], "source": [ "! cat result.txt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arrays" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing array.cpp\n" ] } ], "source": [ "%%file array.cpp\n", "\n", "#include \n", "using std::cout;\n", "using std::endl;\n", " \n", "int main() {\n", " \n", " int N = 3;\n", " double counts[N];\n", " \n", " counts[0] = 1;\n", " counts[1] = 3;\n", " counts[2] = 3;\n", "\n", " double avg = (counts[0] + counts[1] + counts[2])/3;\n", " \n", " cout << avg << endl; \n", "}" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o array.exe array.cpp " ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.33333\n" ] } ], "source": [ "%%bash\n", "\n", "./array.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loops" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing loop.cpp\n" ] } ], "source": [ "%%file loop.cpp\n", "\n", "#include \n", "using std::cout;\n", "using std::endl;\n", "using std::begin;\n", "using std::end;\n", "\n", "int main() \n", "{\n", " int x[] = {1, 2, 3, 4, 5};\n", "\n", " cout << \"\\nTraditional for loop\\n\";\n", " for (int i=0; i < sizeof(x)/sizeof(x[0]); i++) {\n", " cout << i << endl;\n", " }\n", "\n", " cout << \"\\nUsing iterators\\n\";\n", " for (auto it=begin(x); it != end(x); it++) {\n", " cout << *it << endl;\n", " }\n", " \n", " cout << \"\\nRanged for loop\\n\\n\";\n", " for (auto const &i : x) {\n", " cout << i << endl;\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o loop.exe loop.cpp -std=c++14" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Traditional for loop\n", "0\n", "1\n", "2\n", "3\n", "4\n", "\n", "Using iterators\n", "1\n", "2\n", "3\n", "4\n", "5\n", "\n", "Ranged for loop\n", "\n", "1\n", "2\n", "3\n", "4\n", "5\n" ] } ], "source": [ "%%bash\n", "\n", "./loop.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Function arguments\n", "\n", "- A value argument means that the argument is copied in the body of the function\n", "- A referene argument means that the addresss of the value is useed in the function. Reference or pointer arugments are used to avoid copying large objects." ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing func_arg.cpp\n" ] } ], "source": [ "%%file func_arg.cpp\n", "\n", "#include \n", "using std::cout;\n", "using std::endl;\n", " \n", "// Value parameter\n", "void f1(int x) {\n", " x *= 2;\n", " cout << \"In f1 : x=\" << x << endl;\n", "}\n", "\n", "// Reference parameter\n", "void f2(int &x) {\n", " x *= 2;\n", " cout << \"In f2 : x=\" << x << endl;\n", "}\n", "\n", "/* Note\n", "If you want to avoid side effects \n", "but still use references to avoid a copy operation\n", "use a const refernece like this to indicate that x cannot be changed\n", "\n", "void f2(const int &x) \n", "*/\n", "\n", "/* Note \n", "Raw pointers are prone to error and \n", "generally avoided in modern C++\n", "See unique_ptr and shared_ptr\n", "*/\n", "\n", "// Raw pointer parameter\n", "void f3(int *x) {\n", " *x *= 2;\n", " cout << \"In f3 : x=\" << *x << endl;\n", "}\n", " \n", "int main() {\n", " int x = 1;\n", " \n", " cout << \"Before f1: x=\" << x << \"\\n\";\n", " f1(x);\n", " cout << \"After f1 : x=\" << x << \"\\n\";\n", " \n", " cout << \"Before f2: x=\" << x << \"\\n\";\n", " f2(x);\n", " cout << \"After f2 : x=\" << x << \"\\n\";\n", " \n", " cout << \"Before f3: x=\" << x << \"\\n\";\n", " f3(&x);\n", " cout << \"After f3 : x=\" << x << \"\\n\";\n", "}" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "c++ -o func_arg.exe func_arg.cpp --std=c++14" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Before f1: x=1\n", "In f1 : x=2\n", "After f1 : x=1\n", "Before f2: x=1\n", "In f2 : x=2\n", "After f2 : x=2\n", "Before f3: x=2\n", "In f3 : x=4\n", "After f3 : x=4\n" ] } ], "source": [ "%%bash \n", "\n", "./func_arg.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arrays, pointers and dynamic memory\n", "\n", "A pointer is a number that represents an address in computer memory. What is stored at the address is a bunch of binary numbers. How those binary numbers are interpetedd depends on the type of the pointer. To get the value at the pointer adddress, we *derefeernce* the pointer using `*ptr`. Pointers are often used to indicagte the start of a block of value - the name of a plain C-style array is essentialy a pointer to the start of the array.\n", "\n", "For example, the argument `char** argv` means that `argv` has type pointer to pointer to `char`. The pointer to `char` can be thought of as an array of `char`, hence the argument is also sometimes written as `char* argv[]` to indicate pointer to `char` array. So conceptually, it refers to an array of `char` arrays - or a colleciton of strings.\n", "\n", "We generally avoid using raw pointers in C++, but this is standard in C and you should at least understand what is going on.\n", "\n", "In C++, we typically use smart pointers, STL containers or convenient array constructs provided by libraries such as Eigen and Armadillo." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Pointers and addresses" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing p01.cpp\n" ] } ], "source": [ "%%file p01.cpp\n", "\n", "#include \n", "\n", "using std::cout;\n", "\n", "int main() {\n", " int x = 23;\n", " int *xp;\n", " xp = &x;\n", " \n", " cout << \"x \" << x << \"\\n\";\n", " cout << \"Address of x \" << &x << \"\\n\";\n", " cout << \"Pointer to x \" << xp << \"\\n\";\n", " cout << \"Value at pointer to x \" << *xp << \"\\n\";\n", "}" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x 23\n", "Address of x 0x7ffef073c6d4\n", "Pointer to x 0x7ffef073c6d4\n", "Value at pointer to x 23\n" ] } ], "source": [ "%%bash\n", "\n", "g++ -o p01.exe p01.cpp -std=c++14\n", "./p01.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Arrays" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing p02.cpp\n" ] } ], "source": [ "%%file p02.cpp\n", "\n", "#include \n", "\n", "using std::cout;\n", "using std::begin;\n", "using std::end;\n", "\n", "int main() {\n", " int xs[] = {1,2,3,4,5};\n", " \n", " int ys[3];\n", " for (int i=0; i<5; i++) {\n", " ys[i] = i*i;\n", " }\n", " \n", " for (auto x=begin(xs); x!=end(xs); x++) {\n", " cout << *x << \" \";\n", " }\n", " cout << \"\\n\";\n", " \n", " for (auto x=begin(ys); x!=end(ys); x++) {\n", " cout << *x << \" \";\n", " }\n", " cout << \"\\n\";\n", "}" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "16 2 3 4 5 \n", "0 1 4 \n" ] } ], "source": [ "%%bash\n", "\n", "g++ -o p02.exe p02.cpp -std=c++14\n", "./p02.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dynamic memory\n", "\n", "- Use `new` and `delete` for dynamic memory allocation in C++.\n", "- Do not use the C style `malloc`, `calloc` and `free`\n", "- Abosolutely never mix the C++ and C style dynamic memory allocation" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing p03.cpp\n" ] } ], "source": [ "%%file p03.cpp\n", "\n", "#include \n", "\n", "using std::cout;\n", "using std::begin;\n", "using std::end;\n", "\n", "int main() {\n", " \n", " // declare memory \n", " int *z = new int; // single integer\n", " *z = 23;\n", " \n", " // Allocate on heap\n", " int *zs = new int[3]; // array of 3 integers\n", " for (int i=0; i<3; i++) {\n", " zs[i] = 10*i;\n", " }\n", "\n", " cout << *z << \"\\n\";\n", " \n", " for (int i=0; i < 3; i++) {\n", " cout << zs[i] << \" \";\n", " }\n", " cout << \"\\n\";\n", " \n", " // need for manual management of dynamically assigned memory\n", " delete z;\n", " delete[] zs; \n", "}" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "23\n", "0 10 20 \n" ] } ], "source": [ "%%bash\n", "\n", "g++ -o p03.exe p03.cpp -std=c++14\n", "./p03.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Pointer arithmetic\n", "\n", "When you increemnt or decrement an array, it moves to the preceding or next locaion in memory as aprpoprite for the pointer type. You can also add or substract an number, since that is equivalent to mulitple increments/decrements. This is know as pointer arithmetic." ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing p04.cpp\n" ] } ], "source": [ "%%file p04.cpp\n", "\n", "#include \n", "\n", "using std::cout;\n", "using std::begin;\n", "using std::end;\n", "\n", "int main() {\n", " int xs[] = {100,200,300,400,500,600,700,800,900,1000};\n", " \n", " cout << xs << \": \" << *xs << \"\\n\";\n", " cout << &xs << \": \" << *xs << \"\\n\";\n", " cout << &xs[3] << \": \" << xs[3] << \"\\n\";\n", " cout << xs+3 << \": \" << *(xs+3) << \"\\n\"; \n", "}" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0x7ffe7687db60: 100\n", "0x7ffe7687db60: 100\n", "0x7ffe7687db6c: 400\n", "0x7ffe7687db6c: 400\n" ] } ], "source": [ "%%bash\n", "\n", "g++ -std=c++11 -o p04.exe p04.cpp\n", "./p04.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### C style dynamic memory for jagged array (\"matrix\")" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing p05.cpp\n" ] } ], "source": [ "%%file p05.cpp\n", "\n", "#include \n", "\n", "using std::cout;\n", "using std::begin;\n", "using std::end;\n", "\n", "int main() {\n", " int m = 3;\n", " int n = 4;\n", " int **xss = new int*[m]; // assign memory for m pointers to int\n", " for (int i=0; i\n", "\n", "double add(double x, double y) {\n", " return x + y;\n", "}\n", "\n", "double mult(double x, double y) {\n", " return x * y;\n", "}\n", "\n", "int main() {\n", " double a = 3;\n", " double b = 4;\n", " \n", " std::cout << add(a, b) << std::endl;\n", " std::cout << mult(a, b) << std::endl;\n", " \n", "}" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7\n", "12\n" ] } ], "source": [ "%%bash\n", "\n", "g++ -o func01.exe func01.cpp -std=c++14\n", "./func01.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Function parameters\n", "\n", "In the example below, the space allocated *inside* a function is deleted *outside* the function. Such code in practice will almost certainly lead to memory leakage. This is why C++ functions often put the *output* as an argument to the function, so that all memory allocation can be controlled outside the function.\n", "\n", "```\n", "void add(double *x, double *y, double *res, n)\n", "```" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing func02.cpp\n" ] } ], "source": [ "%%file func02.cpp\n", "\n", "#include \n", "\n", "double* add(double *x, double *y, int n) {\n", " double *res = new double[n];\n", " \n", " for (int i=0; i\n", "using std::cout;\n", "\n", "// Using value\n", "void foo1(int x) {\n", " x = x + 1;\n", "}\n", "\n", "\n", "// Using pointer\n", "void foo2(int *x) {\n", " *x = *x + 1;\n", "}\n", "\n", "// Using ref\n", "void foo3(int &x) {\n", " x = x + 1;\n", "}\n", "\n", "int main() {\n", " int x = 0;\n", " \n", " cout << x << \"\\n\";\n", " foo1(x);\n", " cout << x << \"\\n\";\n", " foo2(&x);\n", " cout << x << \"\\n\";\n", " foo3(x);\n", " cout << x << \"\\n\";\n", "}" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0\n", "0\n", "1\n", "2\n" ] } ], "source": [ "%%bash\n", "\n", "g++ -o func03.exe func03.cpp -std=c++14\n", "./func03.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generic programming with templates\n", "\n", "In C, you need to write a *different* function for each input type - hence resulting in duplicated code like\n", "\n", "```C\n", "int iadd(int a, int b)\n", "float fadd(float a, float b)\n", "```\n", "\n", "In C++, you can make functions *generic* by using *templates*.\n", "\n", "Note: When you have a template function, the entire funciton must be written in the header file, and not the source file. Hence, heavily templated libaries are often \"header-only\"." ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing template.cpp\n" ] } ], "source": [ "%%file template.cpp\n", "\n", "#include \n", "\n", "template\n", "T add(T a, T b) {\n", " return a + b;\n", "}\n", "\n", "int main() {\n", " int m =2, n =3;\n", " double u = 2.5, v = 4.5;\n", " \n", " std::cout << add(m, n) << std::endl;\n", " std::cout << add(u, v) << std::endl;\n", "}" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o template.exe template.cpp" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5\n", "7\n" ] } ], "source": [ "%%bash\n", "\n", "./template.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Anonymous functions" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing lambda.cpp\n" ] } ], "source": [ "%%file lambda.cpp\n", "\n", "#include \n", "using std::cout;\n", "using std::endl;\n", "\n", "int main() {\n", "\n", " int a = 3, b = 4;\n", " int c = 0;\n", "\n", " // Lambda function with no capture\n", " auto add1 = [] (int a, int b) { return a + b; };\n", " // Lambda function with value capture\n", " auto add2 = [c] (int a, int b) { return c * (a + b); };\n", " // Lambda funciton with reference capture \n", " auto add3 = [&c] (int a, int b) { return c * (a + b); };\n", "\n", " // Change value of c after function definition\n", " c += 5;\n", "\n", " cout << \"Lambda function\\n\";\n", " cout << add1(a, b) << endl;\n", " cout << \"Lambda function with value capture\\n\";\n", " cout << add2(a, b) << endl;\n", " cout << \"Lambda function with reference capture\\n\";\n", " cout << add3(a, b) << endl;\n", "\n", "}" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "c++ -o lambda.exe lambda.cpp --std=c++14" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lambda function\n", "7\n", "Lambda function with value capture\n", "0\n", "Lambda function with reference capture\n", "35\n" ] } ], "source": [ "%%bash\n", "\n", "./lambda.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Function pointers" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing func_pointer.cpp\n" ] } ], "source": [ "%%file func_pointer.cpp\n", "\n", "#include \n", "#include \n", "#include \n", "\n", "using std::cout;\n", "using std::endl;\n", "using std::function;\n", "using std::vector;\n", "\n", "int main() \n", "{\n", " cout << \"\\nUsing generalized function pointers\\n\";\n", " using func = function;\n", "\n", " auto f1 = [](double x, double y) { return x + y; };\n", " auto f2 = [](double x, double y) { return x * y; };\n", " auto f3 = [](double x, double y) { return x + y*y; };\n", "\n", " double x = 3, y = 4;\n", "\n", " vector funcs = {f1, f2, f3,};\n", "\n", " for (auto& f : funcs) {\n", " cout << f(x, y) << \"\\n\";\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o func_pointer.exe func_pointer.cpp -std=c++14" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Using generalized function pointers\n", "7\n", "12\n", "19\n" ] } ], "source": [ "%%bash\n", "\n", "./func_pointer.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Standard template library (STL)\n", "\n", "The STL provides templated containers and gneric algorithms acting on these containers with a consistent API." ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing stl.cpp\n" ] } ], "source": [ "%%file stl.cpp\n", "\n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "using std::vector;\n", "using std::map;\n", "using std::unordered_map;\n", "using std::string;\n", "using std::cout;\n", "using std::endl;\n", " \n", "struct Point{\n", " int x;\n", " int y;\n", " \n", " Point(int x_, int y_) : \n", " x(x_), y(y_) {};\n", "};\n", "\n", "int main() {\n", " vector v1 = {1,2,3};\n", " v1.push_back(4);\n", " v1.push_back(5);\n", " \n", " cout << \"Vecotr\" << endl;\n", " for (auto n: v1) {\n", " cout << n << endl;\n", " }\n", " cout << endl;\n", " \n", " vector v2;\n", " v2.push_back(Point(1, 2));\n", " v2.emplace_back(3,4);\n", " \n", " cout << \"Vector\" << endl;\n", " for (auto p: v2) {\n", " cout << \"(\" << p.x << \", \" << p.y << \")\" << endl;\n", " }\n", " cout << endl;\n", " \n", " map v3 = {{\"foo\", 1}, {\"bar\", 2}};\n", " v3[\"hello\"] = 3;\n", " v3.insert({\"goodbye\", 4}); \n", " \n", " // Note the a C++ map is ordered\n", " // Note using (traditional) iterators instead of ranged for loop\n", " cout << \"Map\" << endl;\n", " for (auto iter=v3.begin(); iter != v3.end(); iter++) {\n", " cout << iter->first << \": \" << iter->second << endl;\n", " }\n", " cout << endl;\n", " \n", " unordered_map v4 = {{\"foo\", 1}, {\"bar\", 2}};\n", " v4[\"hello\"] = 3;\n", " v4.insert({\"goodbye\", 4}); \n", " \n", " // Note the unordered_map is similar to Python' dict.'\n", " // Note using ranged for loop with const ref to avoid copying or mutation\n", " cout << \"Unordered_map\" << endl;\n", " for (const auto& i: v4) {\n", " cout << i.first << \": \" << i.second << endl;\n", " }\n", " cout << endl;\n", "}" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o stl.exe stl.cpp -std=c++14" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vecotr\n", "1\n", "2\n", "3\n", "4\n", "5\n", "\n", "Vector\n", "(1, 2)\n", "(3, 4)\n", "\n", "Map\n", "bar: 2\n", "foo: 1\n", "goodbye: 4\n", "hello: 3\n", "\n", "Unordered_map\n", "goodbye: 4\n", "hello: 3\n", "bar: 2\n", "foo: 1\n", "\n" ] } ], "source": [ "%%bash\n", "\n", "./stl.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## STL algorithms" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing stl_algorithm.cpp\n" ] } ], "source": [ "%%file stl_algorithm.cpp\n", "\n", "#include \n", "#include \n", "#include \n", "\n", "using std::cout;\n", "using std::endl;\n", "using std::vector;\n", "using std::begin;\n", "using std::end;\n", " \n", "int main() {\n", " vector v(10);\n", "\n", " // iota is somewhat like range\n", " std::iota(v.begin(), v.end(), 1);\n", " \n", " for (auto i: v) {\n", " cout << i << \" \";\n", " }\n", " cout << endl;\n", " \n", " // C++ version of reduce \n", " cout << std::accumulate(begin(v), end(v), 0) << endl;\n", " \n", " // Accumulate with lambda\n", " cout << std::accumulate(begin(v), end(v), 1, [](int a, int b){return a * b; }) << endl;\n", "}" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o stl_algorithm.exe stl_algorithm.cpp -std=c++14" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 3 4 5 6 7 8 9 10 \n", "55\n", "3628800\n" ] } ], "source": [ "%%bash\n", "\n", "./stl_algorithm.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Random numbers" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing random.cpp\n" ] } ], "source": [ "%%file random.cpp\n", "\n", "#include \n", "#include \n", "#include \n", "\n", "using std::cout;\n", "using std::random_device;\n", "using std::mt19937;\n", "using std::default_random_engine;\n", "using std::uniform_int_distribution;\n", "using std::poisson_distribution;\n", "using std::student_t_distribution;\n", "using std::bind;\n", " \n", "// start random number engine with fixed seed\n", "// Note default_random_engine may give differnet values on different platforms\n", "// default_random_engine re(1234);\n", "\n", "// or\n", "// Using a named engine will work the same on differnt platforms\n", "// mt19937 re(1234);\n", "\n", "// start random number generator with random seed\n", "random_device rd;\n", "mt19937 re(rd());\n", "\n", "uniform_int_distribution uniform(1,6); // lower and upper bounds\n", "poisson_distribution poisson(30); // rate\n", "student_t_distribution t(10); // degrees of freedom \n", "\n", "int main() \n", "{\n", " cout << \"\\nGenerating random numbers\\n\";\n", "\n", " auto runif = bind (uniform, re);\n", " auto rpois = bind(poisson, re);\n", " auto rt = bind(t, re);\n", "\n", " for (int i=0; i<10; i++) {\n", " cout << runif() << \", \" << rpois() << \", \" << rt() << \"\\n\";\n", "\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o random.exe random.cpp -std=c++14" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Generating random numbers\n", "3, 31, -0.770332\n", "3, 27, -0.242753\n", "6, 42, 0.635808\n", "1, 22, -1.28388\n", "6, 27, 1.84322\n", "4, 22, 0.90192\n", "5, 27, 0.2381\n", "5, 23, -0.755602\n", "6, 33, 0.389624\n", "6, 21, 1.29621\n" ] } ], "source": [ "%%bash\n", "\n", "./random.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Numerics\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using Armadillo" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing test_arma.cpp\n" ] } ], "source": [ "%%file test_arma.cpp\n", "\n", "#include \n", "#include \n", "\n", "using std::cout;\n", "using std::endl;\n", " \n", "int main() \n", "{\n", " using namespace arma;\n", "\n", " vec u = linspace(0,1,5);\n", " vec v = ones(5);\n", " mat A = randu(4,5); // uniform random deviates\n", " mat B = randn(4,5); // normal random deviates\n", "\n", " cout << \"\\nVecotrs in Armadillo\\n\";\n", " cout << u << endl;\n", " cout << v << endl;\n", " cout << u.t() * v << endl;\n", "\n", " cout << \"\\nRandom matrices in Armadillo\\n\";\n", " cout << A << endl;\n", " cout << B << endl;\n", " cout << A * B.t() << endl;\n", " cout << A * v << endl;\n", "\n", " cout << \"\\nQR in Armadillo\\n\";\n", " mat Q, R;\n", " qr(Q, R, A.t() * A);\n", " cout << Q << endl;\n", " cout << R << endl;\n", "}" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o test_arma.exe test_arma.cpp -std=c++14 -larmadillo" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Vecotrs in Armadillo\n", " 0\n", " 0.2500\n", " 0.5000\n", " 0.7500\n", " 1.0000\n", "\n", " 1.0000\n", " 1.0000\n", " 1.0000\n", " 1.0000\n", " 1.0000\n", "\n", " 2.5000\n", "\n", "\n", "Random matrices in Armadillo\n", " 0.7868 0.0193 0.5206 0.1400 0.4998\n", " 0.2505 0.4049 0.3447 0.5439 0.4194\n", " 0.7107 0.2513 0.2742 0.5219 0.7443\n", " 0.9467 0.0227 0.5610 0.8571 0.2492\n", "\n", " -0.7674 -0.0953 0.4285 0.3620 0.0415\n", " -1.1120 0.0613 1.4152 -1.9853 0.1661\n", " -0.7436 -1.6618 -0.0841 1.9332 -0.9602\n", " -0.6272 -0.2744 0.6118 0.0490 -1.3933\n", "\n", " -0.3111 -0.3320 -0.8700 -0.8698\n", " 0.1312 -0.7760 -0.2394 -0.6150\n", " -0.2320 -1.2993 -0.6748 -1.3584\n", " -0.1677 -1.9175 0.6289 -0.5619\n", "\n", " 1.9665\n", " 1.9633\n", " 2.5024\n", " 2.6367\n", "\n", "\n", "QR in Armadillo\n", " -0.6734 0.5087 0.2592 0.0105 -0.4696\n", " -0.1024 -0.6686 -0.2105 0.1467 -0.6904\n", " -0.3950 0.1098 -0.7627 0.4187 0.2738\n", " -0.4618 -0.2996 -0.1503 -0.7862 0.2373\n", " -0.4083 -0.4386 0.5332 0.4302 0.4142\n", "\n", " -3.0934e+00 -6.5241e-01 -1.8687e+00 -2.3279e+00 -2.0254e+00\n", " 0e+00 -2.4110e-01 -4.0694e-02 -2.1686e-01 -2.5067e-01\n", " 0e+00 0e+00 -6.0467e-02 -1.0163e-01 9.8203e-02\n", " 0e+00 0e+00 0e+00 -2.1243e-01 1.2173e-01\n", " 0e+00 0e+00 0e+00 0e+00 3.6082e-16\n", "\n" ] } ], "source": [ "%%bash\n", "\n", "./test_arma.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using Eigen" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting test_eigen.cpp\n" ] } ], "source": [ "%%file test_eigen.cpp\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "using std::cout;\n", "using std::endl;\n", "using std::ofstream;\n", " \n", "using std::default_random_engine;\n", "using std::normal_distribution;\n", "using std::bind;\n", " \n", "// start random number engine with fixed seed\n", "default_random_engine re{12345};\n", "\n", "normal_distribution norm(5,2); // mean and standard deviation\n", "auto rnorm = bind(norm, re); \n", " \n", "int main() \n", "{\n", " using namespace Eigen;\n", "\n", " VectorXd x1(6);\n", " x1 << 1, 2, 3, 4, 5, 6;\n", " VectorXd x2 = VectorXd::LinSpaced(6, 1, 2);\n", " VectorXd x3 = VectorXd::Zero(6);\n", " VectorXd x4 = VectorXd::Ones(6);\n", " VectorXd x5 = VectorXd::Constant(6, 3);\n", " VectorXd x6 = VectorXd::Random(6);\n", " \n", " double data[] = {6,5,4,3,2,1};\n", " Map x7(data, 6);\n", "\n", " VectorXd x8 = x6 + x7;\n", " \n", " MatrixXd A1(3,3);\n", " A1 << 1 ,2, 3,\n", " 4, 5, 6,\n", " 7, 8, 9;\n", " MatrixXd A2 = MatrixXd::Constant(3, 4, 1);\n", " MatrixXd A3 = MatrixXd::Identity(3, 3);\n", " \n", " Map A4(data, 3, 2);\n", " \n", " MatrixXd A5 = A4.transpose() * A4;\n", " MatrixXd A6 = x7 * x7.transpose();\n", " MatrixXd A7 = A4.array() * A4.array();\n", " MatrixXd A8 = A7.array().log();\n", " MatrixXd A9 = A8.unaryExpr([](double x) { return exp(x); });\n", " MatrixXd A10 = MatrixXd::Zero(3,4).unaryExpr([](double x) { return rnorm(); });\n", "\n", " VectorXd x9 = A1.colwise().norm();\n", " VectorXd x10 = A1.rowwise().sum();\n", " \n", " MatrixXd A11(x1.size(), 3);\n", " A11 << x1, x2, x3;\n", " \n", " MatrixXd A12(3, x1.size());\n", " A12 << x1.transpose(),\n", " x2.transpose(),\n", " x3.transpose();\n", " \n", " JacobiSVD svd(A10, ComputeThinU | ComputeThinV);\n", " \n", " \n", " cout << \"x1: comman initializer\\n\" << x1.transpose() << \"\\n\\n\";\n", " cout << \"x2: linspace\\n\" << x2.transpose() << \"\\n\\n\";\n", " cout << \"x3: zeors\\n\" << x3.transpose() << \"\\n\\n\";\n", " cout << \"x4: ones\\n\" << x4.transpose() << \"\\n\\n\";\n", " cout << \"x5: constant\\n\" << x5.transpose() << \"\\n\\n\";\n", " cout << \"x6: rand\\n\" << x6.transpose() << \"\\n\\n\";\n", " cout << \"x7: mapping\\n\" << x7.transpose() << \"\\n\\n\";\n", " cout << \"x8: element-wise addition\\n\" << x8.transpose() << \"\\n\\n\";\n", " \n", " cout << \"max of A1\\n\";\n", " cout << A1.maxCoeff() << \"\\n\\n\";\n", " cout << \"x9: norm of columns of A1\\n\" << x9.transpose() << \"\\n\\n\";\n", " cout << \"x10: sum of rows of A1\\n\" << x10.transpose() << \"\\n\\n\"; \n", " \n", " cout << \"head\\n\";\n", " cout << x1.head(3).transpose() << \"\\n\\n\";\n", " cout << \"tail\\n\";\n", " cout << x1.tail(3).transpose() << \"\\n\\n\";\n", " cout << \"slice\\n\";\n", " cout << x1.segment(2, 3).transpose() << \"\\n\\n\";\n", " \n", " cout << \"Reverse\\n\";\n", " cout << x1.reverse().transpose() << \"\\n\\n\";\n", " \n", " cout << \"Indexing vector\\n\";\n", " cout << x1(0);\n", " cout << \"\\n\\n\";\n", " \n", " cout << \"A1: comma initilizer\\n\";\n", " cout << A1 << \"\\n\\n\";\n", " cout << \"A2: constant\\n\";\n", " cout << A2 << \"\\n\\n\";\n", " cout << \"A3: eye\\n\";\n", " cout << A3 << \"\\n\\n\";\n", " cout << \"A4: mapping\\n\";\n", " cout << A4 << \"\\n\\n\";\n", " cout << \"A5: matrix multiplication\\n\";\n", " cout << A5 << \"\\n\\n\";\n", " cout << \"A6: outer product\\n\";\n", " cout << A6 << \"\\n\\n\"; \n", " cout << \"A7: element-wise multiplication\\n\";\n", " cout << A7 << \"\\n\\n\"; \n", " cout << \"A8: ufunc log\\n\";\n", " cout << A8 << \"\\n\\n\"; \n", " cout << \"A9: custom ufucn\\n\";\n", " cout << A9 << \"\\n\\n\"; \n", " cout << \"A10: custom ufunc for normal deviates\\n\";\n", " cout << A10 << \"\\n\\n\"; \n", " cout << \"A11: np.c_\\n\";\n", " cout << A11 << \"\\n\\n\"; \n", " cout << \"A12: np.r_\\n\";\n", " cout << A12 << \"\\n\\n\"; \n", " \n", " cout << \"2x2 block startign at (0,1)\\n\";\n", " cout << A1.block(0,1,2,2) << \"\\n\\n\";\n", " cout << \"top 2 rows of A1\\n\";\n", " cout << A1.topRows(2) << \"\\n\\n\";\n", " cout << \"bottom 2 rows of A1\";\n", " cout << A1.bottomRows(2) << \"\\n\\n\";\n", " cout << \"leftmost 2 cols of A1\";\n", " cout << A1.leftCols(2) << \"\\n\\n\";\n", " cout << \"rightmost 2 cols of A1\";\n", " cout << A1.rightCols(2) << \"\\n\\n\";\n", "\n", " cout << \"Diagonal elements of A1\\n\";\n", " cout << A1.diagonal() << \"\\n\\n\";\n", " A1.diagonal() = A1.diagonal().array().square();\n", " cout << \"Transforming diagonal eelemtns of A1\\n\";\n", " cout << A1 << \"\\n\\n\";\n", " \n", " cout << \"Indexing matrix\\n\";\n", " cout << A1(0,0) << \"\\n\\n\";\n", " \n", " cout << \"singular values\\n\";\n", " cout << svd.singularValues() << \"\\n\\n\";\n", " \n", " cout << \"U\\n\";\n", " cout << svd.matrixU() << \"\\n\\n\";\n", " \n", " cout << \"V\\n\";\n", " cout << svd.matrixV() << \"\\n\\n\";\n", "}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "import os\n", "if not os.path.exists('./eigen'):\n", " ! git clone https://gitlab.com/libeigen/eigen.git" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -o test_eigen.exe test_eigen.cpp -std=c++11 -I./eigen" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x1: comman initializer\n", "1 2 3 4 5 6\n", "\n", "x2: linspace\n", " 1 1.2 1.4 1.6 1.8 2\n", "\n", "x3: zeors\n", "0 0 0 0 0 0\n", "\n", "x4: ones\n", "1 1 1 1 1 1\n", "\n", "x5: constant\n", "3 3 3 3 3 3\n", "\n", "x6: rand\n", " 0.680375 -0.211234 0.566198 0.59688 0.823295 -0.604897\n", "\n", "x7: mapping\n", "6 5 4 3 2 1\n", "\n", "x8: element-wise addition\n", " 6.68038 4.78877 4.5662 3.59688 2.82329 0.395103\n", "\n", "max of A1\n", "9\n", "\n", "x9: norm of columns of A1\n", "8.12404 9.64365 11.225\n", "\n", "x10: sum of rows of A1\n", " 6 15 24\n", "\n", "head\n", "1 2 3\n", "\n", "tail\n", "4 5 6\n", "\n", "slice\n", "3 4 5\n", "\n", "Reverse\n", "6 5 4 3 2 1\n", "\n", "Indexing vector\n", "1\n", "\n", "A1: comma initilizer\n", "1 2 3\n", "4 5 6\n", "7 8 9\n", "\n", "A2: constant\n", "1 1 1 1\n", "1 1 1 1\n", "1 1 1 1\n", "\n", "A3: eye\n", "1 0 0\n", "0 1 0\n", "0 0 1\n", "\n", "A4: mapping\n", "6 3\n", "5 2\n", "4 1\n", "\n", "A5: matrix multiplication\n", "77 32\n", "32 14\n", "\n", "A6: outer product\n", "36 30 24 18 12 6\n", "30 25 20 15 10 5\n", "24 20 16 12 8 4\n", "18 15 12 9 6 3\n", "12 10 8 6 4 2\n", " 6 5 4 3 2 1\n", "\n", "A7: element-wise multiplication\n", "36 9\n", "25 4\n", "16 1\n", "\n", "A8: ufunc log\n", "3.58352 2.19722\n", "3.21888 1.38629\n", "2.77259 0\n", "\n", "A9: custom ufucn\n", "36 9\n", "25 4\n", "16 1\n", "\n", "A10: custom ufunc for normal deviates\n", "5.22353 6.16474 3.67474 6.18264\n", "3.81868 4.07998 3.52576 3.82792\n", "3.74872 5.76697 5.3517 2.85655\n", "\n", "A11: np.c_\n", " 1 1 0\n", " 2 1.2 0\n", " 3 1.4 0\n", " 4 1.6 0\n", " 5 1.8 0\n", " 6 2 0\n", "\n", "A12: np.r_\n", " 1 2 3 4 5 6\n", " 1 1.2 1.4 1.6 1.8 2\n", " 0 0 0 0 0 0\n", "\n", "2x2 block startign at (0,1)\n", "2 3\n", "5 6\n", "\n", "top 2 rows of A1\n", "1 2 3\n", "4 5 6\n", "\n", "bottom 2 rows of A14 5 6\n", "7 8 9\n", "\n", "leftmost 2 cols of A11 2\n", "4 5\n", "7 8\n", "\n", "rightmost 2 cols of A12 3\n", "5 6\n", "8 9\n", "\n", "Diagonal elements of A1\n", "1\n", "5\n", "9\n", "\n", "Transforming diagonal eelemtns of A1\n", " 1 2 3\n", " 4 25 6\n", " 7 8 81\n", "\n", "Indexing matrix\n", "1\n", "\n", "singular values\n", "15.8886\n", "2.58818\n", "0.54229\n", "\n", "U\n", " -0.67345 0.607383 0.421368\n", "-0.479529 0.0748696 -0.874326\n", "-0.562598 -0.790873 0.240837\n", "\n", "V\n", " -0.46939 0.190799 -0.433197\n", "-0.588634 -0.197482 0.773183\n", "-0.451663 -0.670964 -0.452453\n", "-0.478731 0.68877 -0.099069\n", "\n" ] } ], "source": [ "%%bash\n", "\n", "./test_eigen.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Check SVD" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "A10 = np.array([\n", " [5.17237, 3.73572, 6.29422, 6.55268],\n", " [5.33713, 3.88883, 1.93637, 4.39812],\n", " [8.22086, 6.94502, 6.36617, 6.5961]\n", "])\n", "\n", "U, s, Vt = np.linalg.svd(A10, full_matrices=False)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([19.50007376, 2.80674189, 1.29869186])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-0.55849978, -0.75124103, -0.3517313 ],\n", " [-0.40681745, 0.61759344, -0.67311062],\n", " [-0.72289526, 0.2328417 , 0.65054376]])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "U" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-0.56424535, 0.47194895, -0.04907563],\n", " [-0.44558625, 0.43195279, 0.45157518],\n", " [-0.45667231, -0.73048295, 0.48064272],\n", " [-0.52395657, -0.23890509, -0.75010267]])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Vt.T" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Probability distributions and statistics\n", "\n", "A nicer library for working with probability distributions. Show integration with Armadillo. Integration with Eigen is also possible." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cloning into 'stats'...\n", "remote: Enumerating objects: 6248, done.\u001b[K\n", "remote: Total 6248 (delta 0), reused 0 (delta 0), pack-reused 6248\u001b[K\n", "Receiving objects: 100% (6248/6248), 1.29 MiB | 0 bytes/s, done.\n", "Resolving deltas: 100% (5468/5468), done.\n", "Checking connectivity... done.\n", "Cloning into 'gcem'...\n", "remote: Enumerating objects: 5, done.\u001b[K\n", "remote: Counting objects: 100% (5/5), done.\u001b[K\n", "remote: Compressing objects: 100% (5/5), done.\u001b[K\n", "remote: Total 2153 (delta 0), reused 1 (delta 0), pack-reused 2148\u001b[K\n", "Receiving objects: 100% (2153/2153), 435.40 KiB | 0 bytes/s, done.\n", "Resolving deltas: 100% (1668/1668), done.\n", "Checking connectivity... done.\n" ] } ], "source": [ "import os\n", "\n", "if not os.path.exists('./stats'):\n", " ! git clone https://github.com/kthohr/stats.git\n", "if not os.path.exists('./gcem'):\n", " ! git clone https://github.com/kthohr/gcem.git" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing stats.cpp\n" ] } ], "source": [ "%%file stats.cpp\n", "#define STATS_ENABLE_STDVEC_WRAPPERS\n", "#define STATS_ENABLE_ARMA_WRAPPERS\n", "// #define STATS_ENABLE_EIGEN_WRAPPERS\n", "#include \n", "#include \n", "#include \"stats.hpp\"\n", "\n", "using std::cout;\n", "using std::endl;\n", "using std::vector;\n", "\n", "// set seed for randome engine to 1776\n", "std::mt19937_64 engine(1776);\n", " \n", "int main() {\n", " // evaluate the normal PDF at x = 1, mu = 0, sigma = 1\n", " double dval_1 = stats::dnorm(1.0,0.0,1.0);\n", "\n", " // evaluate the normal PDF at x = 1, mu = 0, sigma = 1, and return the log value\n", " double dval_2 = stats::dnorm(1.0,0.0,1.0,true);\n", "\n", " // evaluate the normal CDF at x = 1, mu = 0, sigma = 1\n", " double pval = stats::pnorm(1.0,0.0,1.0);\n", "\n", " // evaluate the Laplacian quantile at p = 0.1, mu = 0, sigma = 1\n", " double qval = stats::qlaplace(0.1,0.0,1.0);\n", "\n", " // draw from a normal distribution with mean 100 and sd 15\n", " double rval = stats::rnorm(100, 15);\n", " \n", " // Use with std::vectors\n", " vector pois_rvs = stats::rpois >(1, 10, 3);\n", " cout << \"Poisson draws with rate=3 inton std::vector\" << endl;\n", " for (auto &x : pois_rvs) {\n", " cout << x << \", \";\n", " }\n", " cout << endl;\n", " \n", " \n", " // Example of Armadillo usage: only one matrix library can be used at a time\n", " arma::mat beta_rvs = stats::rbeta(5,5,3.0,2.0);\n", " // matrix input\n", " arma::mat beta_cdf_vals = stats::pbeta(beta_rvs,3.0,2.0);\n", " \n", " /* Example of Eigen usage: only one matrix library can be used at a time\n", " Eigen::MatrixXd gamma_rvs = stats::rgamma(10, 5,3.0,2.0);\n", " */\n", " \n", " cout << \"evaluate the normal PDF at x = 1, mu = 0, sigma = 1\" << endl;\n", " cout << dval_1 << endl; \n", "\n", " cout << \"evaluate the normal PDF at x = 1, mu = 0, sigma = 1, and return the log value\" << endl;\n", " cout << dval_2 << endl;\n", " cout << \"evaluate the normal CDF at x = 1, mu = 0, sigma = 1\" << endl;\n", " cout << pval << endl;\n", "\n", " cout << \"evaluate the Laplacian quantile at p = 0.1, mu = 0, sigma = 1\" << endl;\n", " cout << qval << endl;\n", "\n", " cout << \"draw from a normal distribution with mean 100 and sd 15\" << endl;\n", " cout << rval << endl;\n", " \n", " \n", " \n", " cout << \"draws from a beta distribuiotn to populate Armadillo matrix\" << endl;\n", " cout << beta_rvs << endl;\n", " \n", " cout << \"evaluaate CDF for beta draws from Armadillo inputs\" << endl;\n", " cout << beta_cdf_vals << endl;\n", " \n", " /* If using Eigen\n", " cout << \"draws from a Gamma distribuiotn to populate Eigen matrix\" << endl;\n", " cout << gamma_rvs << endl;\n", " */\n", "}" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "\n", "g++ -std=c++11 -I./stats/include -I./gcem/include -I./eigen stats.cpp -o stats.exe" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Poisson draws with rate=3 inton std::vector\n", "1, 3, 5, 6, 1, 3, 1, 1, 2, 3, \n", "evaluate the normal PDF at x = 1, mu = 0, sigma = 1\n", "0.241971\n", "evaluate the normal PDF at x = 1, mu = 0, sigma = 1, and return the log value\n", "-1.41894\n", "evaluate the normal CDF at x = 1, mu = 0, sigma = 1\n", "0.841345\n", "evaluate the Laplacian quantile at p = 0.1, mu = 0, sigma = 1\n", "-1.60944\n", "draw from a normal distribution with mean 100 and sd 15\n", "120.059\n", "draws from a beta distribuiotn to populate Armadillo matrix\n", " 0.6504 0.3613 0.5206 0.5956 0.6657\n", " 0.4931 0.0844 0.5793 0.3354 0.1347\n", " 0.8770 0.2274 0.7872 0.6645 0.5689\n", " 0.6129 0.9087 0.8134 0.6172 0.6005\n", " 0.6285 0.8030 0.6545 0.5177 0.3090\n", "\n", "evaluaate CDF for beta draws from Armadillo inputs\n", " 0.5637 0.1375 0.3440 0.4677 0.5909\n", " 0.3022 0.0023 0.4398 0.1130 0.0088\n", " 0.9234 0.0390 0.7993 0.5887 0.4222\n", " 0.4975 0.9558 0.8394 0.5051 0.4760\n", " 0.5249 0.8237 0.5710 0.3395 0.0906\n", "\n" ] } ], "source": [ "%%bash\n", "\n", "./stats.exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Solution to exercise**" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing greet.cpp\n" ] } ], "source": [ "%%file greet.cpp\n", "\n", "#include \n", "#include \n", "using std::string;\n", "using std::cout;\n", "\n", "int main(int argc, char* argv[]) {\n", " string name = argv[1];\n", " int n = std::stoi(argv[2]);\n", " \n", " for (int i=0; i