{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Types\n",
    "\n",
    "This notebook provides (a) a brief introduction to types (for instance, integers, bools and strings), (b) how to test types and (c) convert from one type to another."
   ]
  },
  {
   "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\n",
    "\n",
    "include(\"jlFiles/printmat.jl\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Some Important Types\n",
    "\n",
    "Julia has many different types of variables: signed integers (like 2 or -5), floating point numbers (2.0 and -5.1), bools (false/true), bitarrays (similar to bools, but with more efficient use of memory), strings (\"hello\"), Dates (2017-04-23) and many more. \n",
    "\n",
    "The numerical types also comes with subtypes for different precisions, for instance, Float16, Float32 and Float64. Unless you specify otherwise, code like\n",
    "```\n",
    "a = 2\n",
    "b = 2.0\n",
    "```\n",
    "gives an Int64 and a Float64 respectively (at least on the 64 bit version of Julia)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Integers and Floats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: Int64\n",
      "2\n",
      "\n",
      "b: Float64\n",
      "2.0\n",
      "\n",
      "A: Vector{Int64}\n",
      "     1    \n",
      "     2    \n",
      "\n",
      "B: Vector{Float64}\n",
      "     1.000\n",
      "     2.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "a = 2                   #integer, Int (Int64 on most machines)\n",
    "b = 2.0                 #floating point, (Float64 on most machines)\n",
    "A = [1;2]\n",
    "B = [1.0;2.0]\n",
    "\n",
    "println(\"a: \",typeof(a))\n",
    "println(a)\n",
    "\n",
    "println(\"\\nb: \",typeof(b))\n",
    "println(b)\n",
    "\n",
    "println(\"\\nA: \",typeof(A))\n",
    "printmat(A)\n",
    "\n",
    "println(\"B: \",typeof(B))\n",
    "printmat(B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Why Use Int When There Are Floats? \n",
    "\n",
    "That is, why bother with sometimes using 3 when you could use 3.0 everywhere? Mostly because you cannot use 3.0 everywhere... \n",
    "\n",
    "For instance, you cannot pick out element `x[3.0]` from a vector. It has to be `x[3]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n"
     ]
    }
   ],
   "source": [
    "x = [1,10,100,1000]\n",
    "\n",
    "#println(x[3.0])    #uncomment and run. Will give an error\n",
    "\n",
    "println(x[3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bools and BitArrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "c: Bool\n",
      "true\n",
      "\n",
      "C: BitVector\n",
      "     0    \n",
      "     1    \n",
      "\n",
      "A BitArray is a more economical array version of Bool, but prints as 0/1 by printmat.\n",
      "Notice that typeof(C[1]) gives: Bool\n"
     ]
    }
   ],
   "source": [
    "c = 2 > 1.1\n",
    "println(\"c: \",typeof(c))\n",
    "println(c)\n",
    "\n",
    "C = A .> 1.5\n",
    "println(\"\\nC: \",typeof(C))\n",
    "printmat(C)\n",
    "\n",
    "println(\"A BitArray is a more economical array version of Bool, but prints as 0/1 by printmat.\\n\",\n",
    "        \"Notice that typeof(C[1]) gives: \",typeof(C[1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Char and Strings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Char\n",
      "String\n"
     ]
    }
   ],
   "source": [
    "t = 'a'                                    #Char, just one letter\n",
    "println(typeof(t))\n",
    "\n",
    "txt = \"Dogs are nicer than cats.\"          #String, could be a long novel\n",
    "println(typeof(txt))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Calculations with Mixed Types and Converting Types\n",
    "\n",
    "A calculation like \"integer\" + \"float\" works and the type of the result is a float (the more flexible type). Similarly, \"bool\" + \"integer\" will give an integer. \n",
    "\n",
    "There are also direct ways of converting a variable from one type to another using the `convert()` function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Some Calculations with Mixed Types (\"promotion\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Int + Float64: 3.0\n",
      "Bool + Int: 3\n"
     ]
    }
   ],
   "source": [
    "println(\"Int + Float64: \",1+2.0)\n",
    "println(\"Bool + Int: \",(1 .> 0) + 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Converting from Int to Float and Vice Versa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x: Vector{Float64}\n",
      "     1.100\n",
      "    10.100\n",
      "   100.100\n",
      "\n",
      "rounding x to Int: Vector{Int64}\n",
      "     1    \n",
      "    10    \n",
      "   100    \n",
      "\n",
      "A: Vector{Int64}\n",
      "     1    \n",
      "     2    \n",
      "\n",
      "after converting A to Float64: Vector{Float64}\n",
      "     1.000\n",
      "     2.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "x = [1.1;10.1;100.1]\n",
    "println(\"x: \",typeof(x))\n",
    "printmat(x)\n",
    "\n",
    "B_to_Int = round.(Int,x)                     #Float64 -> Int by rounding\n",
    "println(\"rounding x to Int: \",typeof(B_to_Int))\n",
    "printmat(B_to_Int)\n",
    "\n",
    "A = [1;2]\n",
    "println(\"A: \",typeof(A))\n",
    "printmat(A)\n",
    "\n",
    "A_to_Float64 = convert.(Float64,A)            #Int -> Float64\n",
    "println(\"after converting A to Float64: \",typeof(A_to_Float64))\n",
    "printmat(A_to_Float64)                        #Float64.(A) also works"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Converting from Bools and BitArrays to Int and Vice Versa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vector{Int64}\n",
      "     0    \n",
      "     1    \n",
      "\n",
      "BitVector\n",
      "     1    \n",
      "     0    \n",
      "     1    \n",
      "\n"
     ]
    }
   ],
   "source": [
    "C = A .> 1.5\n",
    "C_to_Int = convert.(Int,C)               #BitArray -> Int\n",
    "println(typeof(C_to_Int))                #Int.(C) also works\n",
    "printmat(C_to_Int)\n",
    "\n",
    "D = [1;0;1]\n",
    "D_to_Bool = convert.(Bool,D)              #Int -> BitArray\n",
    "println(typeof(D_to_Bool))                #Bool.(D) also works\n",
    "printmat(D_to_Bool)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## From Bools and BitArrays to Int: A Tricky Case (extra)\n",
    "\n",
    "`false` is a \"strong zero\" in the sense that \n",
    "`false*NaN == 0` and `false*Inf == 0`. \n",
    "\n",
    "If you do not want that behaviour in your code, transform `false` to 0 and then multiply."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0\n",
      "0.0\n",
      "NaN\n",
      "NaN\n"
     ]
    }
   ],
   "source": [
    "println(false*NaN)\n",
    "println(false*Inf)\n",
    "\n",
    "println(convert(Int,false)*NaN)\n",
    "println(convert(Int,false)*Inf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Testing the Type\n",
    "\n",
    "The perhaps easiest way to test the type is by using the `isa(variable,Type)` function. The type can be a union of other types (see below for an example). \n",
    "\n",
    "Notice that an array has the type `Array` and more specifically, `Array{Float64}` if it is an array with Float64 numbers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.2 is a Number: true\n",
      "1.2 is an Int: false\n",
      "1.2 is an Int or a Float64: true\n",
      "[1.2, 1.3] is a Float64: false\n",
      "[1.2, 1.3] is an Array: true\n"
     ]
    }
   ],
   "source": [
    "x = 1.2\n",
    "z = [1.2,1.3]\n",
    "\n",
    "println(\"$x is a Number: \",isa(x,Number))\n",
    "println(\"$x is an Int: \",isa(x,Int))\n",
    "println(\"$x is an Int or a Float64: \",isa(x,Union{Int,Float64}))\n",
    "\n",
    "println(\"$z is a Float64: \",isa(z,Float64))\n",
    "println(\"$z is an Array: \",isa(z,Array))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Type Instability (extra)\n",
    "\n",
    "Your code will often run faster if your variables do not change type in the computations. For instance, do *not* do something like this\n",
    "```\n",
    "x = 0                  #instead, do x = 0.0\n",
    "x = x + 0.1\n",
    "```\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "kernelspec": {
   "display_name": "Julia 1.6.0",
   "language": "julia",
   "name": "julia-1.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
