{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tricky Stuff\n",
    "\n",
    "This file highlights some tricky aspects of Julia (from the perspective of a Matlab user)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load Packages and Extra Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "printyellow (generic function with 1 method)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using Printf, LinearAlgebra\n",
    "\n",
    "include(\"jlFiles/printmat.jl\")   #function for prettier matrix printing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# An Nx1 Array Is Not a Vector\n",
    "\n",
    "and it sometimes matters. \n",
    "\n",
    "Julia has both vectors and Nx1 arrays (the latter being a special case of NxM arrays). They can often be used interchangeably, but not always."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "v and v2 look similar:\n",
      "     1    \n",
      "     1    \n",
      "\n",
      "     1    \n",
      "     1    \n",
      "\n",
      "but they have different sizes: (2,) (2, 1)\n"
     ]
    }
   ],
   "source": [
    "v  = ones(Int,2)                     #a vector with two elements\n",
    "v2 = ones(Int,2,1)                   #a 2x1 matrix (Array)\n",
    "\n",
    "println(\"v and v2 look similar:\")\n",
    "printmat(v)\n",
    "printmat(v2)\n",
    "println(\"but they have different sizes: \",size(v),\" \",size(v2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# X[1,:] Gives a (Flat) Vector\n",
    "\n",
    "If `X` is a $T\\times n$ matrix, then `X[1,:]` gives a flat vector, *not* a $1xn$ matrix (or row vector)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "size of X[1,:]: (2,)\n",
      "    11    \n",
      "    12    \n",
      "\n",
      "size of X[1:1,:]: (1, 2)\n",
      "    11        12    \n",
      "\n"
     ]
    }
   ],
   "source": [
    "X = [11 12;21 22]\n",
    "\n",
    "x1 = X[1,:]                      #this gives a flat vector\n",
    "println(\"size of X[1,:]: \", size(x1))\n",
    "printmat(x1)\n",
    "\n",
    "x1b = X[1:1,:]                   #this gives a 1x2 matrix\n",
    "println(\"size of X[1:1,:]: \", size(x1b))\n",
    "printmat(x1b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Array .+ Scalar Requires a Dot (.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "     2    \n",
      "     3    \n",
      "\n"
     ]
    }
   ],
   "source": [
    "y = [1;2] .+ 1              #do not forget the dot (.)\n",
    "printmat(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Creating Variables in a Loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tor: 0.28366218546322625\n",
      "Oden: 0.28366218546322625\n"
     ]
    }
   ],
   "source": [
    "for i = 1:5\n",
    "    global Tor         #without this, Tor is not seen outside the loop\n",
    "    Tor = cos(i)\n",
    "end\n",
    "println(\"Tor: $Tor\")\n",
    "\n",
    "\n",
    "Oden = Inf\n",
    "for i = 1:5\n",
    "    #global Oden        #only needed in script\n",
    "    Oden = cos(i)       #will overwrite an existing value \n",
    "end\n",
    "println(\"Oden: $Oden\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Threads.@threads and Variable Scope\n",
    "\n",
    "Code like this\n",
    "```\n",
    "v = 1:2\n",
    "Threads.@threads for i = 1:N\n",
    "    v = something \n",
    "    x = SomeFunction(v)\n",
    "end\n",
    "```\n",
    "can create unexpected results since the threads are sharing `v`. This is solved by declaring `v` inside the loop to be `local`. (Clearly, this only happens when you have configured Julia to use several threads.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "this should always be zero. Run a few times to check if that is true.\n",
      "\n",
      "[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 15.0, 11.0, 16.0, 0.0, 16.0, 16.0, 0.0, 0.0, 6.0, 0.0, 0.0, 19.0, 20.0, 17.0, 0.0, 22.0, 9.0, 0.0, 0.0, 20.0, 23.0, 18.0, 32.0, 22.0, 28.0, 16.0, 28.0, 40.0, 0.0, 20.0, 21.0, 35.0, 30.0, 26.0, 33.0, 27.0, 27.0, 19.0, 47.0, 12.0, 0.0, 35.0, 0.0, 31.0, 51.0, 50.0, 31.0, 29.0, 0.0, 50.0, 47.0, 39.0, 53.0, 49.0, 19.0, 66.0, 59.0, 46.0, 38.0, 50.0, 66.0, 39.0, 67.0, 40.0, 0.0, 69.0, 36.0, 68.0, 73.0, 43.0, 67.0, 77.0, 65.0, 78.0, 47.0, 80.0, 56.0, 32.0, 61.0, 81.0, 70.0, 67.0, 76.0, 88.0, 90.0, 81.0]\n"
     ]
    }
   ],
   "source": [
    "function f2(N)\n",
    "  v = falses(N+1)\n",
    "  x = zeros(Int,N,N)\n",
    "  Threads.@threads for i = 1:N\n",
    "    #local v                   #uncomment to solve the problem\n",
    "    v    = falses(N)\n",
    "    v[i] = true\n",
    "    x[v,i] .= i\n",
    "  end\n",
    "  return x\n",
    "end\n",
    "\n",
    "println(\"this should always be zero. Run a few times to check if that is true.\\n\")\n",
    "M = 100\n",
    "dev = zeros(M)\n",
    "for i = 1:M\n",
    "  dev[i] = maximum(abs,f2(i) - diagm(1:i))\n",
    "end\n",
    "println(dev)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A Heterogeneous Array"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To create a 'cell array' (a heterogeneous Array), use `[x1,x2,...]`\n",
    "\n",
    "Alternatively, you can preallocate as in `B = Array{Any}(undef,3)` and then fill by, for instance, `B[1] = [11 12]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "The array A: \n",
      "    11        12    \n",
      "    21        22    \n",
      "\n",
      "A nice dog\n",
      "\n",
      "    27    \n",
      "\n",
      "\n",
      "The array B: \n",
      "    11        12    \n",
      "    21        22    \n",
      "\n",
      "A nice dog\n",
      "\n",
      "    27    \n",
      "\n"
     ]
    }
   ],
   "source": [
    "A = [[11 12;21 22],\"A nice dog\",27]\n",
    "\n",
    "println(\"\\nThe array A: \")\n",
    "foreach(i->printmat(A[i]),1:length(A))     #print each element of A\n",
    "\n",
    "B = Array{Any}(undef,3)\n",
    "B[1] = [11 12]\n",
    "B[2] = \"A bad cat\"\n",
    "B[3] = pi\n",
    "\n",
    "println(\"\\nThe array B: \")\n",
    "foreach(i->printmat(A[i]),1:length(A))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# An Array of Arrays\n",
    "\n",
    "can be initialized by comprehension (see below). (Do *not* use fill. See \"A Reshaped Array\" for why.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x[1]\n",
      "   -99.000     0.000\n",
      "     0.000     0.000\n",
      "\n",
      "x[2]\n",
      "     0.000     0.000\n",
      "     0.000     0.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "x = [zeros(2,2) for i=1:2]        #a vector of two matrices\n",
    "x[1][1,1] = -99                   #change an element of x[1]\n",
    "\n",
    "println(\"x[1]\")\n",
    "printmat(x[1])\n",
    "\n",
    "println(\"x[2]\")\n",
    "printmat(x[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Arrays are Different...\n",
    "\n",
    "Vectors and matrices (arrays) can take lots of memory space, so **Julia is designed to avoid unnecessary copies of arrays**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 1. B = A Creates Two Names of the Same Array\n",
    "\n",
    "If A is an array, then\n",
    "```\n",
    "B = A\n",
    "```\n",
    "creates two names of the *same* array. If you later change A, then B is changed automatically. (Similarly, if you change B, then A is changed automatically.) To avoid this, do\n",
    "```\n",
    "C = copy(A)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "old A,B,C (each is a column): \n",
      "         A         B         C\n",
      "         1         1         1\n",
      "         2         2         2\n",
      "\n",
      "after changing A[2] to -999, A,B,C are:\n",
      "         A         B         C\n",
      "         1         1         1\n",
      "      -999      -999         2\n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mNotice that B changed, but C did not.\u001b[22m\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "A = [1,2]\n",
    "B = A                                 #A and B are the same\n",
    "C = copy(A)                           #A and C are not the same\n",
    "println(\"old A,B,C (each is a column): \")\n",
    "printmat([A B C],colNames=[\"A\",\"B\",\"C\"],prec=0)\n",
    "\n",
    "A[2] = -999\n",
    "println(\"after changing A[2] to -999, A,B,C are:\")\n",
    "printmat([A B C],colNames=[\"A\",\"B\",\"C\"],prec=0)\n",
    "\n",
    "printblue(\"\\nNotice that B changed, but C did not.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 2. A Reshaped Array Still Refers to the Original Array\n",
    "\n",
    "Let `A` be an array. If you create a reshaped array by either \n",
    "```\n",
    "B = reshape(A,n,m)\n",
    "C = vec(A)\n",
    "D = A'\n",
    "E = fill(A,2)\n",
    "```\n",
    "then A, B, C, D and E contain the same values. Changing one changes the others automatically.\n",
    "\n",
    "This can be 'fixed' by using `copy()`\n",
    "```\n",
    "B = copy(reshape(A,n,m))\n",
    "C = copy(vec(A))\n",
    "D = copy(A')\n",
    "E = [copy(A) for i=1:2]\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "original A: \n",
      "     1         2    \n",
      "\n",
      "old B, C and D (each is a column): \n",
      "         B         C         D\n",
      "         1         1         1\n",
      "         2         2         2\n",
      "\n",
      "B, C and D after changing element A[2] to -999\n",
      "         B         C         D\n",
      "         1         1         1\n",
      "      -999      -999      -999\n",
      "\n",
      "E[1] and E[2] after changing element A[2] to -999\n",
      "     1      -999    \n",
      "\n",
      "     1      -999    \n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mNotice that B, C, D and E also changed\u001b[22m\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "A = [1 2]\n",
    "println(\"original A: \")\n",
    "printmat(A)\n",
    "\n",
    "B = reshape(A,2,1)\n",
    "C = vec(A)\n",
    "D = A'\n",
    "E = fill(A,2)\n",
    "\n",
    "println(\"old B, C and D (each is a column): \")\n",
    "printmat([B C D],colNames=[\"B\",\"C\",\"D\"],prec=0)\n",
    "\n",
    "A[2] = -999\n",
    "println(\"B, C and D after changing element A[2] to -999\")\n",
    "printmat([B C D],colNames=[\"B\",\"C\",\"D\"],prec=0)\n",
    "println(\"E[1] and E[2] after changing element A[2] to -999\")\n",
    "printmat(E[1])\n",
    "printmat(E[2])\n",
    "\n",
    "printblue(\"\\nNotice that B, C, D and E also changed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 3. Changing an Array Inside a Function Can Have Effects *Outside* the Function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you use an array as a function argument, then it is passed as a reference.\n",
    "\n",
    "This means that if you change some elements of the array (`A[1] = A[1]/2`, say) inside the function, then it will also affect the array outside the function (even if they have different names). \n",
    "\n",
    "In contrast, if you change the entire array (`A/2`, say) inside the function, then that does not affect the array outside the function.\n",
    "\n",
    "If you really need an independent copy of an array, create it by\n",
    "\n",
    "`B = copy(A)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "original x:\n",
      "     1.000     2.000\n",
      "\n",
      "x (outside function) after calling f1(x):\n",
      "     0.500     2.000\n",
      "\n",
      "\n",
      "original x:\n",
      "     1.000     2.000\n",
      "\n",
      "x (outside function) after calling f2(x):\n",
      "     1.000     2.000\n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mNotice that f1() changed x also outside the function, but f2() did not.\u001b[22m\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "function f1(A)\n",
    "    A[1] = A[1]/2          #changes ELEMENTS of A, affects outside value\n",
    "  return A\n",
    "end\n",
    "function f2(A)\n",
    "    A = A/2                #changes all of A, does not affect outside value\n",
    "  return A\n",
    "end\n",
    "\n",
    "x  = [1.0 2.0]\n",
    "printlnPs(\"original x:\")\n",
    "printmat(x)\n",
    "\n",
    "y1 = f1(x)\n",
    "printlnPs(\"x (outside function) after calling f1(x):\")\n",
    "printmat(x)\n",
    "\n",
    "x  = [1.0 2.0]\n",
    "printlnPs(\"\\noriginal x:\")\n",
    "printmat(x)\n",
    "\n",
    "y2 = f2(x)\n",
    "printlnPs(\"x (outside function) after calling f2(x):\")\n",
    "printmat(x)\n",
    "\n",
    "printblue(\"\\nNotice that f1() changed x also outside the function, but f2() did not.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 4. Arrays in Arrays Still Refer to the Underlying Arrays\n",
    "\n",
    "An array `a` inside another array `A`is really just a reference to the existing `a`. Changing elements of `a` will change `A`.\n",
    "\n",
    "Again, this can be 'fixed' by using `copy(a)` when creating `A`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A:\n",
      "    11        12    \n",
      "    21        22    \n",
      "\n",
      "A nice dog\n",
      "\n",
      "    27    \n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mChange a[1,1] to -999 and notice that also A changes\u001b[22m\u001b[39m\n",
      "A:\n",
      "  -999        12    \n",
      "    21        22    \n",
      "\n",
      "A nice dog\n",
      "\n",
      "    27    \n",
      "\n"
     ]
    }
   ],
   "source": [
    "a = [11 12;21 22]\n",
    "A = [a,\"A nice dog\",27]               #try copy(a) instead\n",
    "println(\"A:\")\n",
    "foreach(i->printmat(A[i]),1:length(A))\n",
    "\n",
    "printblue(\"\\nChange a[1,1] to -999 and notice that also A changes\")\n",
    "a[1,1] = -999\n",
    "println(\"A:\")\n",
    "foreach(i->printmat(A[i]),1:length(A))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Julia 1.6.1",
   "language": "julia",
   "name": "julia-1.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
