# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .jl
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Julia 1.6.0
#     language: julia
#     name: julia-1.6
# ---

# # Chapter-9 Standard Libraries
# This notebook contains the sample source code explained in the book *Hands-On Julia Programming, <NAME>, 2021, bpb Publications. All Rights Reserved*.

using Pkg
pkg"activate ."
pkg"instantiate"

# ## 9.1 Introduction
#
# There are various functions and methods that are part of Julia already. A developer needs to use them directly rather than having to code for them. This chapter is a bird's eyeview of such methods. 

# ### Modules
#
# Namespaces where a method or a type reside. Using a method or type outside the module may require explicit qualification. 

# +
module m1
    function f()
        println("Defined as m1.f")
    end
    function g()
        println("Defined as m1.g")
    end
    function h()
        println("Defined as m1.h")
    end
    export f
end

module m2
    function f()
        println("Defined as m2.f")
    end
    function g()
        println("Defined as m2.g")
    end
    export g
end
# -

names(m1)

names(m2)

f()

m1.f()

m2.f()

using .m1

f()

g()

using Main.m2

g()

using .m1: g

g()

using .m1: h

h()

# ## 9.2 Standard Modules
#
# `Core`, `Base` and `Main` the bare minimum modules of a Julia shell. `m1` and `m2` here are submodules of `Main`.

varinfo()

x = 1

varinfo()

# ## 9.3 System
#
# The submodule that interacts with the operating system. System command execution being one of them. 

run(`cmd /c dir`)

Sys.isunix(), Sys.iswindows()

# ## 9.4 Filesystem
#
# File organization and manipulation (not read and write). 

old_dir = pwd()

cd("..")

pwd()

cd(old_dir)

pwd()

cd(".."); pwd()

readdir()  #Same as readdir(pwd())

for (root, dirs, files) in walkdir("Chapter-09")
    println("Directories in $root")
    for dir in dirs
        println(joinpath(root, dir)) # path to directories
    end
    println("Files in $root")
    for file in files
        println(joinpath(root, file)) # path to files
    end
end

cd(old_dir); pwd()

fs = stat("Chapter-9 Standard Libraries.ipynb")

# ## 9.5 Parallel Programming
#
# Execution of programs in tandem in various execution environments, yet be able to obtain meaningful and synchronized results. Performance and resource utilization are two major outcomes of this kind of programming. 

# ### Asynchronous Programming
#
# Breaking the code into small chunks of executable pieces that can be executed in parallel. Depending on the kind of processing permitted by the hardware and OS, these may run in one execution thread or multiple.

# #### Tasks

t = Task() do 
    sleep(10)
    println("done")
end

schedule(t)

t = Task() do 
    sleep(10)
    println("done")
end

schedule(t); wait(t)

t = @task begin
    sleep(5)
    println("done")
end

@sync  begin
    sleep(5)
    println("done")
end

# #### Channels

c = Channel(8)

c = Channel{Int}(4)

# +
@async begin
    for i = 1:100
        println("Adding $i to channel")
        put!(c, i)
    end
end

@async begin
    for i = 1:100
        v = take!(c)
        println("Removing value: $v")
    end
end
# -

close(c)

# ### Multithreading
#
# Julia can be made to run on a multiple threads mode. In such a conditions the tasks can be parallelized on these threads. Synchronization objects can be used across the executing threads. 

Threads.nthreads()

Threads.@spawn for i=1:100
    sleep(1)
    println("Step: $i")
end

# ### Distributed Computing
#
# This is cluster computing infrastrcuture, where a cluster can be configured across Julia processes in the same machine or in another machine. These independent processes can execute independently yet be able to share data, pass messages and communicate over a shared channel. 

# ## 9.6 IO and Network
#
# Julia accesses input and output devices as streams. The network devices are also interfaced like sockets. IO is an important stream based interface to understand. 

# ### Default Stream
#
# `stdin`, `stdout` and `stderr` are the standard streams for input, output and error in most processes. `stdin` integration with Jupyter is limited. All those samples may best be reviewed in a REPL console. 

write(stdout, "Hello World")

write(stdout, "\u2200 x \u2203 y")

# ### Text I/O

# ### Binary I/O

# ### File I/O
#
# Files are also accessed as `IO` streams for both read and write operations. However, the `IO` streams are obtained by `open` call. Any open stream must be closed with a `close` function call. 

write("hello.txt", "Hello World")

fd = open("hello.txt", "r");
data = read(fd);

write(fd, "This is second line")

close(fd)
write(stdout, data)

open("hello.txt", "r") do fd
    data = read(fd)
    write(stdout, data)
end

write("hello.txt", "Hello World");
data = read("hello.txt");
write(stdout, data);

# Files are opened for `read` or `write`. A file opened for reading cannot be written into.  

open("hello.txt", "r") do fd
    while !eof(fd)
        b = read(fd, 1)
        write(stdout, b)
    end
end
rm("hello.txt")

# ### Delimited Files
#
# Generalized command separated value (CSV) kind of files. Used extensively in spreadsheets. Also interfaced in data science applications as an easier way to share data. 

using DelimitedFiles
x = collect(1:100);
y = Float64.(collect(101:200));
open("values.csv", "w") do f
    println(f, "X,Y")
    writedlm(f, [x y], ',')
end

data, header = readdlm("values.csv", ',', header=true);

size(data, 1)

header

rm("values.csv")

# ### Network 
#
# Just like files are opened with open to obtain the `IO` stream to interface, sockets are equivalent network communication end points to obtain connection streams. However, if you could use download method to provide a URL and download files. 

download("https://github.com/JuliaLang/julia/releases/download/v1.5.3/julia-1.5.3.tar.gz", "julia-1.5.3.tar.gz")

using Sockets
@async begin
    server = listen(3000)
    while true
        s = accept(server)
        @async while isopen(s)
            msg = readline(s, keep=true)
            write(s, "Server Response: $msg")
        end  
    end
end

c = connect(3000)
@async while isopen(c)
    println(stdout, readline(c, keep=true))
end

for i = 1:5
    println(c, "Client Msg id: $i")
end

close(c)

# ### Memory I/O
#
# Strings cannot be manipulated. It may help to create a byte array for `IO` in the memory and use that for text manipulations. In the end the byte array can be converted to a String. 

io = IOBuffer()
println(io, "This is Line 1")
println(io, "This is Line 2")
str = String(take!(io))

A = fill(10, (5, 20))
A[2, 3] = 20
open("mmap.bin", "w+") do fd
    write(fd, size(A, 1))
    write(fd, size(A, 2))
    write(fd, A)
end

# Memory mapping is reverse problem. A file can be mapped to memory and accessed selectively page by page. This way the whole file need not be loaded in the memory. 

using Mmap
fd = open("mmap.bin", "r")
nr = read(fd, Int)
nc = read(fd, Int)
A2 = Mmap.mmap(fd, Matrix{Int}, (nr, nc))
println("A2[1, 1]: ", A2[1, 1], " A2[2, 3]: ", A2[2, 3])
close(fd)

A2 = nothing
GC.gc()
rm("mmap.bin")

# ## 9.7 Constants
#
# There are many system and Julia related constants. Mathematical constants are also defined in the standard library. 

VERSION

C_NULL

Sys.BINDIR

Sys.CPU_THREADS

Sys.WORD_SIZE

Sys.KERNEL

Sys.MACHINE

Sys.ARCH

Sys.ENV

MathConstants.pi

MathConstants.e

# ## 9.8 Notable Modules
#
# Miscellaneous modules of Julia library. 

# ### Dates
#
# User renderable date and time representations. In the system the date is just a continuously increasing counter from a datum. These functions make meaningful period associations and provide the correct date and time. 

using Dates

now()

DateTime(2020)

t = DateTime(2020, 10, 31, 01, 02, 03)

Date(2020)

d = Date(t)

DateTime(d)

t > d

DateTime(2020) < now()

DateTime(2020) == Date(2020)

p = Month(3) + Day(10) + Hour(15)

typeof(p)

now() + p

Date(now())+ Day(300)

now() - DateTime(2020)

now() - Date(2020)

Date(now()) - Date(2020)

Year(now())

Month(now())

Day(now())

Week(now())

year(now())

tonext(today()) do d
    dayofweek(d) == Thursday &&
    dayofweekofmonth(d) == 4 &&
    month(d) == November
end

# ### Logging
#
# Long running processes or servers running in Julia need to provide consistent and standard log reporting. This module helps create such reports. 

@debug "This is a debug message $(sum(rand(100)))"

@info "This message is just informational"

@warn "This is a warning message"

@error "This is an error message"

A = ones(3, 4)
@info "A is all ones" A

# The `Logging` module provides interfaces to implement complete loggers. It also provides the ability to override the global logger with a different local one. 

using Logging

open("file.log", "w") do f
    with_logger(SimpleLogger(f)) do
        @info "This is my simple info log"
        @error "This is an error written to file"
        @warn "This is a warning message"
        @info "Current logger is same as global logger" current_logger() == global_logger()
    end
end
write(stdout, read("file.log"));

rm("file.log")

@info "Current logger is same as global logger" current_logger() == global_logger()

# ### Statistics
#
# This module provides simple functions for central tendencies like mean, median and standard deviations etc. Functions for correlations and covariances are also provided. However, the treatment of statistics in thi module are highly rudimentary. 

using Statistics

mean(1:10)

median(1:10)

std(1:10)

var(1:10)

cor(1:10, rand(10))

cor(1:10, 11:20)

cov(1:10, rand(10))

# ### Random Numbers
#
# Provides methods for random number generation and other permutation and combination functions. 

using Random

rand(Int, 2)

rand(2, 3)

rand!(zeros(2, 3))

rand(1:4, (2, 3))

# ## 9.9 Conclusion
#
# ## Exercises

StarcoderdataJupyterScriptsDedupFiltered