From e30c05289fadd72406f255b4051844abfb5c58ea Mon Sep 17 00:00:00 2001
From: YingXing <yingxing.cheng@mathematik.uni-stuttgart.de>
Date: Tue, 10 Oct 2023 13:43:55 +0200
Subject: [PATCH] update exercise

---
 .gitignore       |   1 +
 2_Exercise.ipynb | 637 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 638 insertions(+)
 create mode 100644 2_Exercise.ipynb

diff --git a/.gitignore b/.gitignore
index 0613db0..0d1f673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@
 *.jl.*.cov
 .ipynb_checkpoints
 *.gif
+.DS_Store
diff --git a/2_Exercise.ipynb b/2_Exercise.ipynb
new file mode 100644
index 0000000..b1d7173
--- /dev/null
+++ b/2_Exercise.ipynb
@@ -0,0 +1,637 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b7689bbe",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# import dependencies\n",
+    "using Test"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "debc3515-fa08-4577-82bb-9eced849d616",
+   "metadata": {},
+   "source": [
+    "# Data Types and Multiple Dispatch\n",
+    "\n",
+    "In this notebook, we will explore the fundamental concepts of Julia's type system and understand multiple dispatch, focusing on:\n",
+    "- Abstract and Concrete Types\n",
+    "- Dynamic Types\n",
+    "- Multiple Dispatch\n",
+    "\n",
+    "These concepts enhance Julia's performance, especially in mathematical and scientific computing.\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "66c9e24b-965f-490e-873f-83b4d654d4f2",
+   "metadata": {},
+   "source": [
+    "## Types: Abstract and Concrete\n",
+    "\n",
+    "Julia's type system includes **abstract types** and **concrete types**. \n",
+    "\n",
+    "- **Abstract Types**: Cannot be instantiated and serve to represent general categories of objects.\n",
+    "- **Concrete Types**: Can be instantiated and are used to create objects.\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3885f36f-1ee4-4fa3-aaa0-3df62bcac362",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "abstract type Animal end  # Abstract type\n",
+    "\n",
+    "struct Dog <: Animal  # Concrete type inheriting from Animal\n",
+    "    name::String\n",
+    "    age::Int\n",
+    "end\n",
+    "\n",
+    "struct Cat <: Animal  # Another concrete type inheriting from Animal\n",
+    "    name::String\n",
+    "    age::Int\n",
+    "end\n",
+    "\n",
+    "a_dog = Dog(\"Buddy\", 3)\n",
+    "a_cat = Cat(\"Kitty\", 2)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aa33841d",
+   "metadata": {},
+   "source": [
+    "One can use subtypes to obtain all subtypes of either an abstract type or a concrete type."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9a30f7e1",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "subtypes(Animal)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "06bb4657",
+   "metadata": {},
+   "source": [
+    "We can write a nice function to make the output nicer:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a5204ffa",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\"\"\"\n",
+    "    print_type_tree(T, level=0, max_level=typemax(Int), prefix=\"\", subtype_prefix=\"\")\n",
+    "\n",
+    "Print a tree structure that visualizes the type hierarchy rooted at `T`.\n",
+    "\n",
+    "# Parameters\n",
+    "- `T`: The type which serves as the root of the tree to print.\n",
+    "- `level`: (Optional, default=`0`) An integer specifying the current recursion depth - typically not provided by the user.\n",
+    "- `max_level`: (Optional, default=`typemax(Int)`) An integer specifying the maximum recursion depth, i.e., how many levels deep into the type hierarchy to print.\n",
+    "- `prefix`: (Optional, default=`\"\"`) A string used internally to format the tree structure - typically not provided by the user.\n",
+    "- `subtype_prefix`: (Optional, default=`\"\"`) A string used internally to format the tree structure - typically not provided by the user.\n",
+    "\n",
+    "# Usage\n",
+    "```julia\n",
+    "print_type_tree(Number, max_level=7)\n",
+    "```\n",
+    "\"\"\"\n",
+    "function print_type_tree(T, level=0, max_level=typemax(Int), prefix=\"\", subtype_prefix=\"\") \n",
+    "    # Stop recursion if max level is reached\n",
+    "    if level >= max_level\n",
+    "        return\n",
+    "    end\n",
+    "    \n",
+    "    # Print current type\n",
+    "    println(prefix, subtype_prefix, T)\n",
+    "    \n",
+    "    # Obtain subtypes\n",
+    "    subs = subtypes(T)\n",
+    "    \n",
+    "    # Recursively print subtypes with adjusted prefix\n",
+    "    for (i, subtype) in enumerate(subs)\n",
+    "        new_prefix = \"    \" \n",
+    "        subtype_prefix = i < length(subs) ? \"├── \" : \"└── \"\n",
+    "        \n",
+    "        # If the subtype has no further subtypes, avoid using the \"|\" in the prefix\n",
+    "        if isempty(subtypes(subtype))\n",
+    "            print_type_tree(subtype, level+1, max_level, prefix * \"    \", subtype_prefix)\n",
+    "        else\n",
+    "            print_type_tree(subtype, level+1, max_level, prefix * new_prefix, subtype_prefix)\n",
+    "        end\n",
+    "    end\n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ba91649d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print_type_tree(Animal)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "87a9c84f",
+   "metadata": {},
+   "source": [
+    "We can also use this function to print the subtypes of built-in objects."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "53919ada",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print_type_tree(Number)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1e23272e",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Set max_level to 2 to avoid lengthy output.\n",
+    "print_type_tree(AbstractArray, 0, 2)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "16d371f7",
+   "metadata": {},
+   "source": [
+    "For concrete structs, constructors are special functions that instantiate objects of a particular type, initiating the lifecycle of an object instance. \n",
+    "When an object of a custom type is created, it is initialized via constructors, ensuring that it begins its existence in a valid state.\n",
+    "\n",
+    "### Default Constructors\n",
+    "\n",
+    "The name of a constructor is the same as the name of the type and can have multiple methods, which differ in their argument types. \n",
+    "By default, Julia provides a constructor that accepts arguments for all fields, unless an `inner constructor` is provided, in which case you need to define all needed constructors yourself.\n",
+    "For instance, if you have a type named `MyType`, the constructor(s) for this type will also be named `MyType`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "277c436a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "struct MyType1\n",
+    "    a::Int\n",
+    "    b::Float64\n",
+    "end\n",
+    "\n",
+    "# The default constructor is MyType1\n",
+    "obj_1 = MyType1(1, 2.0)\n",
+    "\n",
+    "@testset \"Test MyType1\" begin\n",
+    "    # Testing with some valid inputs\n",
+    "    obj_1 = MyType1(1, 2.0)\n",
+    "    @test obj_1.a == 1 \n",
+    "    @test obj_1.b == 2.0 \n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2051b0ac",
+   "metadata": {},
+   "source": [
+    "### Inner and Outer Constructors\n",
+    "\n",
+    "Julia features inner and outer constructors. Inner constructors are defined inside the block of the type definition and have access to its private internals, while outer constructors are defined outside and call an inner constructor to create an object."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "51d774f8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "struct MyType2\n",
+    "    a::Int\n",
+    "    b::Float64\n",
+    "    \n",
+    "    # Inner constructor\n",
+    "    function MyType2(a, b)\n",
+    "        a < 0 && throw(ArgumentError(\"a must be non-negative\"))\n",
+    "        new(a, b)\n",
+    "    end\n",
+    "end\n",
+    "\n",
+    "# Outer constructor\n",
+    "MyType2(a::Int) = MyType2(a, 0.0)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b859da95",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "@testset \"Test MyType2\" begin\n",
+    "    @testset \"Innter constructor with valid inputs\" begin\n",
+    "        obj_21 = MyType2(100, 11.34)\n",
+    "        @test obj_21.a == 100 \n",
+    "        @test obj_21.b == 11.34\n",
+    "    end\n",
+    "    @testset \"Inner constructor with invalid inputs\" begin\n",
+    "        @test_throws ArgumentError MyType2(-100, 1.0)\n",
+    "    end\n",
+    "    @testset \"Outer constructor with valid inputs\" begin\n",
+    "        obj_22 = MyType2(2)\n",
+    "        @test obj_22.a == 2\n",
+    "        @test obj_22.b == 0.0 \n",
+    "    end\n",
+    "    @testset \"Outer constructor with invalid inputs\" begin\n",
+    "        @test_throws ArgumentError MyType2(-1)\n",
+    "    end\n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e65f0f9a",
+   "metadata": {},
+   "source": [
+    "In the example, the inner constructor validates that `a` is non-negative, providing a measure of control and validation over the initialization process."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bde87d55",
+   "metadata": {},
+   "source": [
+    "### Parametric Constructors\n",
+    "\n",
+    "Parametric types can also have constructors, with parameters specified in the type definition, offering greater flexibility."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9e5c0f17",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "struct MyType3{T1<:Real, T2<:Real}\n",
+    "    a::T1\n",
+    "    b::T2\n",
+    "    \n",
+    "    # Inner constructor\n",
+    "    function MyType3(a::T1, b::T2) where {T1, T2}\n",
+    "        a < 0 && throw(ArgumentError(\"a must be non-negative\"))\n",
+    "        new{T1, T2}(a, b)\n",
+    "    end\n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "58e3d497",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "@testset \"Test MyType3\" begin\n",
+    "    @testset \"Valid inputs\" begin\n",
+    "        obj_31 = MyType3(1, 2.0)\n",
+    "        @test obj_31.a == 1 \n",
+    "        @test obj_31.b == 2.0 \n",
+    "        obj_32 = MyType3(1.0, 2.0)\n",
+    "        @test obj_32.a == 1.0\n",
+    "        @test obj_32.b == 2.0 \n",
+    "        obj_33 = MyType3(1, 2)\n",
+    "        @test obj_33.a == 1 \n",
+    "        @test obj_33.b == 2\n",
+    "    end\n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8b60210e",
+   "metadata": {},
+   "source": [
+    "Exercise: `MyType3` can only take a real number. Please write `MyType4` to improve it, using the guidelines below:\n",
+    "\n",
+    "- Accept `Number` as inputs.\n",
+    "- Modify the inner constructor to handle `Complex` numbers for argument `a`. Specifically, if a complex number is supplied, bypass any checking mechanisms.\n",
+    "- Add an outer constructor like in `MyType3`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4edd8d4e",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# TODO\n",
+    "struct MyType4\n",
+    "    a\n",
+    "    b\n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "59da2faf",
+   "metadata": {},
+   "source": [
+    "The new MyType4 should pass all of the following tests:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d1cfb791",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "@testset \"Test MyType4\" begin\n",
+    "    @testset \"Inner constructor with valid inputs\" begin\n",
+    "        obj_41 = MyType4(1, 2.0)\n",
+    "        @test obj_41.a == 1 \n",
+    "        @test obj_41.b == 2.0 \n",
+    "        obj_42 = MyType4(1.0, 2.0)\n",
+    "        @test obj_42.a == 1.0\n",
+    "        @test obj_42.b == 2.0 \n",
+    "        obj_43 = MyType4(1, 2)\n",
+    "        @test obj_43.a == 1 \n",
+    "        @test obj_43.b == 2\n",
+    "        obj_44 = MyType4(Complex(1.0, 1.0), 2)\n",
+    "        @test obj_44.a == 1.0 + 1.0im\n",
+    "        @test obj_44.b == 2\n",
+    "    end\n",
+    "    @testset \"Inner constructor with invalid inputs\" begin\n",
+    "        @test_throws ArgumentError MyType4(-1, 1)\n",
+    "    end\n",
+    "    @testset \"Outer constructor with valid inputs\" begin\n",
+    "        @test_throws ArgumentError MyType4(1)\n",
+    "    end\n",
+    "\n",
+    "    @testset \"Outer constructor with invalid inputs\" begin\n",
+    "        @test_throws ArgumentError MyType4(-1)\n",
+    "    end\n",
+    "end"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6bd93a18-6569-4253-a836-0601b77c8bf2",
+   "metadata": {},
+   "source": [
+    "## Dynamical Types"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c7784fbb",
+   "metadata": {},
+   "source": [
+    "Dynamical types in Julia allow variables to change their type dynamically. \n",
+    "Julia uses `dynamic typing`, which means that the type of a variable is checked at runtime."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a77d6d3c-e838-45c7-bd71-249c8547281e",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x = Dog(\"Buddy\", 4)  # x is an Dog\n",
+    "println(typeof(x))\n",
+    "\n",
+    "x = Cat(\"Kitty\", 3)  # Now x is a Cat\n",
+    "println(typeof(x))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bb3f86ee-ec12-41a4-b29c-a622bc95af8d",
+   "metadata": {},
+   "source": [
+    "## Multiple Dispatch of Functions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "37ef5d37",
+   "metadata": {},
+   "source": [
+    "Multiple dispatch is one of Julia's key features. \n",
+    "It allows defining function behavior across many combinations of argument types.\n",
+    "Different method definitions are dispatched based on the types of all function arguments."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6e409d52-290d-4e47-a4ba-5030ff45b66c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Define a generic function speak\n",
+    "speak(animal::Animal) = \"Some generic animal noise\"\n",
+    "\n",
+    "# Use multiple dispatch to define method for specific types\n",
+    "speak(animal::Dog) = \"Woof! I am $(animal.name), a dog.\"\n",
+    "speak(animal::Cat) = \"Meow! I am $(animal.name), a cat.\"\n",
+    "\n",
+    "# Test the methods\n",
+    "println(speak(a_dog))\n",
+    "println(speak(a_cat))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e28a025f",
+   "metadata": {},
+   "source": [
+    "## Union Types"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9dd9a6fa",
+   "metadata": {},
+   "source": [
+    "Union types are a useful feature in Julia that allow a variable to take on values of several different types. \n",
+    "It allows more flexibility when dealing with variables that might have different types in different situations."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9a11afbb",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "const CatOrDog = Union{Cat, Dog}\n",
+    "\n",
+    "function process_input(input::CatOrDog)\n",
+    "    speak(input)\n",
+    "end\n",
+    "\n",
+    "process_input(a_dog)\n",
+    "process_input(a_cat)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5117e6dc",
+   "metadata": {},
+   "source": [
+    "## Parametric Types"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6fddf5da",
+   "metadata": {},
+   "source": [
+    "Parametric types allow you to define types that are parameterized by other types.\n",
+    "It provides the ability to create a function or type that works with different data types."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "daa25661",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "struct Point{T}\n",
+    "    x::T\n",
+    "    y::T\n",
+    "end\n",
+    "\n",
+    "p1 = Point{Int}(1, 2)\n",
+    "p2 = Point{Float64}(1.0, 2.0)\n",
+    "p3 = Point{String}(\"one\", \"two\")\n",
+    "\n",
+    "println(p1)\n",
+    "println(p2)\n",
+    "println(p3)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "408f1815",
+   "metadata": {},
+   "source": [
+    "## Function Overloading"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1b83770e",
+   "metadata": {},
+   "source": [
+    "Function overloading allows you to define different versions of a function for different types or numbers of arguments.\n",
+    "It provides flexibility and improves code readability and performance."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "af6ad638",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add(x::Dog, y::Cat) = x.age + y.age\n",
+    "add(x::Cat, y::Dog) = x.age + y.age\n",
+    "add(x::Dog, y::Dog) = x.age + y.age\n",
+    "add(x::Cat, y::Cat) = x.age + y.age\n",
+    "\n",
+    "println(add(a_dog, a_cat))          # Calls the first version\n",
+    "println(add(a_cat, a_dog))  # Calls the second version"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7f2723a1",
+   "metadata": {},
+   "source": [
+    "The function overloading above can be simplified by the following one using generic function."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "853117bc",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "add(x::Animal, y::Animal) = x.age + y.age"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "705971b5",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "struct Tiger<:Animal\n",
+    "    name::String\n",
+    "    age::Int\n",
+    "end\n",
+    "\n",
+    "a_tiger = Tiger(\"Ravi\", 3)\n",
+    "\n",
+    "print(add(a_tiger, a_dog))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "907ced09",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Julia 1.9.2",
+   "language": "julia",
+   "name": "julia-1.9"
+  },
+  "language_info": {
+   "file_extension": ".jl",
+   "mimetype": "application/julia",
+   "name": "julia"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
-- 
GitLab