text:
# Julia parallel course EGU21

[![Build Status](https://github.com/luraess/julia-parallel-course-EGU21/workflows/CI/badge.svg)](https://github.com/luraess/julia-parallel-course-EGU21/actions)

**[vEGU2021: SC4.6 Solving differential equations in parallel with Julia | Thu, 29 Apr, 16:00–17:00 (CEST)](https://meetingorganizer.copernicus.org/EGU21/session/38986)**

👉 **Organisation notes:**
- 💡 The short course material (available on this repository) **was just updated - fetch the latest versions!**
- 👉 **Make sure to go through [Getting started](docs/getting-started.md) before the start of the course.**
- Further interests in solving PDEs with Julia on GPUs❓
    - Sign-up (free) for a hands-on workshop at [JuliaCon2021](https://juliacon.org/2021/)
    - Check out this [online geo-HPC tutorial](https://github.com/luraess/geo-hpc-course)

----
This short course covers trendy areas in modern geocomputing with broad geoscientific applications. The physical processes governing natural systems' evolution are often mathematically described as systems of differential equations. A performant numerical implementation of the solving algorithm leveraging modern hardware is key and permits to tackle problems that were technically not possible a decade ago.

# Content
* [Objectives](#objectives)
* [About this repository](#about-this-repository)
* [Getting started](#getting-started) _(not part of the live course)_
* 👉 [Short course material](#short-course-material)
* [Extras](#extras) _(not part of the live course)_
* [Further reading](#further-reading)

# Objectives
The goal of this short course is to offer an interactive overview on how to solve systems of (partial) differential equations in parallel on GPUs using the [Julia language]. [Julia] combines high-level language simplicity and low-level language performance. The resulting codes and applications are fast, short and readable \[[1][JuliaCon20a], [2][JuliaCon20b], [3][JuliaCon19]\].

We will design and implement a numerical algorithm that predicts ice flow dynamics over mountainous topography (Greenland) using GPU computing (e.g. Fig. below). We will discretise the shallow ice approximation (SIA) equations in our ice flow solver to assess Greenland's ice cap evolution as function of a climate scenario.

![Greenland ice cap](docs/greenland_1.png)

**The online course consists of 2 parts:**
1. [**Part 1**](#part-1---julia-and-iterative-solvers) - You will learn about the [Julia language] and iterative PDE solvers.
2. [**Part 2**](#part-2---solving-ice-flow-pdes-on-gpus) - You will implement a GPU parallel PDE solver to predict ice flow dynamics on real topography.

By the end of this short course, you will:
- Have an iterative GPU PDE solver that predicts ice-flow;
- Have a Julia code that achieves similar performance than legacy codes (C, CUDA, MPI);
- Know how the Julia language solves the "two-language problem";
- Be able to leverage the computing power of modern GPU accelerated servers and supercomputers.

> 💡 **Disclaimer**
>- The solvers presented in this short course, based on the **pseudo-transient method**, enable to solve PDEs iteratively and are well-suited for parallel execution (on GPUs). It is **not** the purpose of this course to provide an extensive overview of various solution techniques.
>- The performance assessment is done using the time / iteration metric which reflects the ability of the algorithm to efficiently exploit the memory bandwidth of the (parallel) hardware. Further performance considerations regarding the metric can be found [here](https://github.com/omlins/ParallelStencil.jl).

⤴ [_back to content_](#content)

# About this repository
The course repository lists following folders and items:
- the [data](data) folder contains various low resolution Greenland input data (bedrock topography, surface elevation, ice thickness, masks, ...) downscaled from [BedMachine Greenland v3] - note the filenames include grid resolution information `(nx, ny)`;
- the [docs](docs) folder contains documentation linked in the [README](README.md);
- the various _output_ folder will contain the codes output, mainly figures in png format;
- the [scripts](scripts) folder contains the scripts this course is about 🎉
- the [extras](extras) folder contains supporting course material (not discussed live during the course);
- the [`Project.toml`](Project.toml) file is a Julia project file, tracking the used packages and enabling a reproducible environment.

> 👉 This repository is an interactive and dynamic source of information related to the short course.
>- Check out the [**Discussion**](https://github.com/luraess/julia-parallel-course-EGU21/discussions) tab if you have general comments, ideas to share or for Q&A.
>- File an [**Issue**](https://github.com/luraess/julia-parallel-course-EGU21/issues) if you encounter any technical problems with the distributed codes.
>- Interact in a open-minded, respectful and inclusive manner.

⤴ [_back to content_](#content)

# Getting started
> ⚠ Due to the time limitation, the short course will not cover the [Getting started](docs/getting-started.md) steps. These are meant to provide directions to the participant willing to actively try out the examples during the short course. **It is warmly recommended to perform the [Getting started](docs/getting-started.md) steps before the beginning of the workshop.**

**Please follow the detailed steps in**: [docs/getting-started.md](docs/getting-started.md)

The [provided directions](docs/getting-started.md) will get you started with:
1. Installing Julia v1.6
2. Running the scripts from the course repository.

> 👉 **Note: This course was developed and tested on Julia v1.6. It should work with any Julia version ≥v1.6**. The install configuration were tested on a MacBook Pro running macOS 10.15.7, a Linux GPU server running Ubuntu 20.04 LTS and a Linux GPU server running CentOS 8.

# Short course material
This section lists the material discussed within this 60 min. short course:
* [Part 1 - Julia and iterative solvers](#part-1---julia-and-iterative-solvers)
    * [Why Julia](#why-julia)
    * [Diffusion equation](#diffusion-equation)
    * [Iterative solvers](#iterative-solvers)
* [Part 2 - solving ice flow PDEs on GPUs](#part-2---solving-ice-flow-pdes-on-gpus)
    * [SIA equation](#sia-equation)
    * [SIA implementation](#sia-implementation)
    * [GPU SIA implementation](#gpu-sia-implementation)
    * [XPU SIA implementation](#xpu-sia-implementation)
    * [Greenland's ice cap evolution](#greenlands-ice-cap-evolution)

💡 In this course we will implement a 2D nonlinear diffusion equation on GPUs in Julia using the finite-difference method and an iterative solving approach. The goal is to resolve the shallow ice approximation (SIA) and predict ice flow over Greenland.

⤴ [_back to content_](#content)

## Part 1 - Julia and iterative solvers

### Why Julia
_by Mauro Werder_

Julia is a modern, general-purpose programming language unifying interactive, high productivity features (like Python, Matlab, etc.) with high performance (like C, Fortran, etc.).  This removes the need to have a separate prototype and production languages (the _two-language problem_).

The main reason to use Julia for scientific computing is:
- high performance & productivity, ditto
- a good package manager (see, e.g., the [`Project.toml`](Project.toml) of this course) making reproducible science easier
- a rapidly expanding number of packages, with many at the forefront of research (e.g. GPU-packages, differential equations, machine learning, automatic differentiation)
- a friendly community with a large number of scientific users

The main reason to use Julia for GPU computing is that it indeed solves the two-language problem in this domain: it works well from prototyping an idea with a simple serial code to massively parallel, multi-node GPU production code.

A short introduction to Julia will be given using the first numerical example of this course (next section).  A very short summary of features covered:
- third-party packages can be installed with the package manager (see [Package installation](docs/getting-started.md#packages-installation))
- use a package with `using XYZ`
- run the code in a file with `include("abc.jl")`
- index into an array with `[ ]` and starts at 1
- vectorized function application do with the dot-notation, e.g. `sin.(x) ./ y` for vectors `x` and `y`
- macros do funky stuff with your code (aka code-transformations) and call them with `@`, e.g. `@time sin(1)` prints the time it takes to evaluate `sin(1)`
- for raster-data handling we use [GeoData.jl](https://github.com/rafaqz/GeoData.jl) (but other packages also exist)

For more info see https://docs.julialang.org.

⤴ [_back to course material_](#short-course-material)

### Diffusion equation
Let's start with a 1D linear diffusion example to implement both an explicit and iterative implicit PDE solver:

dH/dt = ∇.(D ∇H)

The diffusion of a quantity `H` over time `t` can be described as (1a) a diffusive flux, (1b) a flux balance and (1c) an update rule:
```md
qH    = -D*dH/dx  (1a)
dHdt  =  -dqH/dx  (1b)
dH/dt = dHdt      (1c)
```
The [`diffusion_1D_expl.jl`](scripts/diffusion_1D_expl.jl) code implements an iterative and explicit solution of eq. (1) for an initial Gaussian profile:
```md
H0 = exp(-(x-lx/2.0)^2)
```

![](docs/diffusion_expl.png)

But now, you may ask: can we use an implicit algorithm to side-step the CFL-condition, control the (physically motivated) time steps `dt` _**and**_ keep it "matrix-free" ?

⤴ [_back to course material_](#short-course-material)

### Iterative solvers
_by Ludovic Räss_

The [`diffusion_1D_impl.jl`](scripts/diffusion_1D_impl.jl) code implements an iterative, implicit solution of eq. (1). **How ?** We include the physical time derivative `dH/dt=(H-Hold)/dt` in the previous rate of change `dHdt` to define the residual `ResH`
```md
ResH = -(H-Hold)/dt -dqH/dx
```
and iterate until the values of `ResH` (the residual of the eq. (1)) drop below a defined tolerance level `tol`.

![](docs/diffusion_impl.png)

It works, but the "naive" _Picard_ iteration count seems to be pretty high (`niter>7000`). A efficient way to circumvent this is to add "damping" (`damp`) to the rate-of-change `dHdt`, analogous to add friction enabling faster convergence \[[4][Frankel50]\]
```md
dHdt = ResH + damp*dHdt
```
The [`diffusion_1D_damp.jl`](scripts/diffusion_1D_damp.jl) code implements a damped iterative implicit solution of eq. (1). The iteration count drops to `niter~700`. This pseudo-transient approach enables fast as the iteration count scales close to _O(N)_ and not _O(N^2)_.

![](docs/diffusion_damp.png)

#### Performance considerations
Performance evaluation is a complex topic as different metrics would lead to different conclusions. Ultimately, efficient algorithms should minimise the time to solution. For iterative algorithms this means:
1) Ensure fast iterations (minimise the time per iteration).
2) Keep the iteration count as low as possible and, in particular, that iteration count scales aroung _O(n)_ with the numerical resolution _n_.

We will here report (1) for various implementations on various computer architectures.

⤴ [_back to course material_](#short-course-material)

## Part 2 - solving ice flow PDEs on GPUs

### SIA equation applied to the Greenland Ice Sheet
Let's move from the simple **1D linear diffusion** example to the shallow ice approximation (SIA) equation, a **2D nonlinear diffusion** equation for ice thickness _H_:

  dH/dt = ∇.(D ∇S) + M

with surface elevation _S_ the sum of ice thickness _H_ and bed elevation _B_\
  S = H + B,\
surface mass-balance _M_, and non-linear diffusion coefficient\
  D = a Hⁿ⁺² √(∇S.∇S)ⁿ⁻¹\
where _n_ is Glen's n (take =3) and _a_ is the ice flow factor (1.5e-24 Pa⁻ⁿ s⁻¹).

Writting the equation in pseudo-code:
```md
qHx   = -D*dS/dx                  (2a)
qHy   = -D*dS/dy                  (2b)
dHdt  = -(dqHx/dx + dqHy/dy) + M  (2c)
dH/dt = dHdt                      (2d)
```
with the nonlinear diffusion coefficient `D` with the power-law exponent `npow` (aka Glen's exponent):
```md
D = a*H^(npow+2)*sqrt((dS/dx)^2 + (dS/dy)^2)^(npow-1)
```

The topography data, bedrock elevation and ice thickness, is from the
[BedMachine Greenland v3](https://sites.uci.edu/morlighem/dataproducts/bedmachine-greenland/) dataset (Morlighem et al., (2017)) and is loaded via the [GeoData.jl](https://github.com/rafaqz/GeoData.jl) package (see [`helpers.jl`](scripts/helpers.jl)).

We implement climate forcing using a simple mass balance (accumulation and ablation) `M` formulation:
```md
M  = min(grad_b*(S - z_ELA), b_max)
```
as function of the surface elevation `S` and capped by the maximal accumulation rate `b_max`. The mass balance gradient `grad_b` is defined as
```md
grad_b = (1.3517 - 0.014158*LAT)/100.0*0.91
```
where `LAT` is the latitude (taken from \[[5][Machgut16]\]). The equilibrium line altitude `z_ELA` (where accumulation = ablation) is latitude dependent, ranging from 1300m (South) to 1000m (North) as suggested by \[[5][Machgut16]\].

⤴ [_back to course material_](#short-course-material)

### SIA implementation
The [`iceflow.jl`](scripts/iceflow.jl) code implements the 2D SIA equations using the iterative implicit damped formulation as in [`diffusion_1D_damp.jl`](scripts/diffusion_1D_damp.jl) using the pseudo-transient approach. The calculation of the SIA PDEs resumes in these 13 lines of Julia code:
```julia
# mass balance
M     .= min.(grad_b.*(S .- z_ELA), b_max)
# compute diffusivity
dSdx  .= diff(S, dims=1)/dx
dSdy  .= diff(S, dims=2)/dy
gradS .= sqrt.(av_ya(dSdx).^2 .+ av_xa(dSdy).^2)
D     .= a*av(H).^(npow+2) .* gradS.^(npow-1)
# compute flux
qHx   .= .-av_ya(D).*diff(S[:,2:end-1], dims=1)/dx
qHy   .= .-av_xa(D).*diff(S[2:end-1,:], dims=2)/dy
# update ice thickness
dtau  .= dtausc*min.(10.0, cfl./(epsi .+ av(D)))
ResH  .= .-(diff(qHx, dims=1)/dx .+ diff(qHy, dims=2)/dy) .+ inn(M)
dHdt  .= dHdt.*damp .+ ResH
H[2:end-1,2:end-1] .= max.(0.0, inn(H) .+ dtau.*dHdt)
# apply mask
H[Mask.==0] .= 0.0
# update surface
S     .= B .+ H
```
> 💡 Note that the here discussed SIA codes do not implement any flux limiter scheme to circumvent known accuracy and stability issues. Check out \[[6][Jarosch13], [7][Visnjevic18]\] for further references (the [`iceflow_bench.jl`](extras/iceflow_bench.jl) script implements the benchmark \[[6][Jarosch13]\] that reflects this issue).

The model output is the ice surface elevation, the ice thickness, the ice velocity magnitude and the mass balance:

![](docs/iceflow_out1.png)

This implementation of the SIA equations solves the steady-state (i.e. the physical time derivative being removed as `dt->∞`). The last part of this course ([Greenland's ice cap evolution](#greenland-s-ice-cap-evolution)) will show how to achieve an (implicit) ice flow predictions over a specific time span `dt` by including the physical time derivative in the `ResH` term.

#### CPU Performance
The figure below depicts the time 1000 iterations (or pseudo-time steps) take running the [`iceflow.jl`](scripts/iceflow.jl) code on a Intel Xeon Gold 6244 (1 thread - plain Julia):

![](docs/timingJulia.png)

⤴ [_back to course material_](#short-course-material)

### GPU SIA implementation
So now we have a cool iterative and implicit SIA solver in less than 100 lines of code 🎉. Good enough for low resolution calculations. What if we need higher resolution and faster time to solution ? Parallel and GPU computing makes it possible. Let's start from the [`iceflow.jl`](scripts/iceflow.jl) code and port it to GPU (with some intermediate steps).

The main idea of GPU parallelisation is to calculate each grid point concurently by a different GPU thread (instaed of the more serial CPU execution) as depicted hereafter:

![](docs/cpu_gpu.png)

---

1. Extract the flux `qHx, qHy` from the physics calculations in [`iceflow.jl`](scripts/iceflow.jl):
```julia
# [...] skipped lines
# compute flux
qHx   .= .-av_ya(D).*diff(S[:,2:end-1], dims=1)/dx
qHy   .= .-av_xa(D).*diff(S[2:end-1,:], dims=2)/dy
# [...] skipped lines
```

2. Let's modify these lines into a `compute_flux` function we will then be able to turn into a GPU kernel.
```julia
# [...] skipped lines
function compute_flux!(qHx, qHy, S, D, dx, dy, nx, ny)
    Threads.@threads for iy=1:ny
        for ix=1:nx
            if (ix<=nx-1 && 2<=iy<=ny-1)  qHx[ix,iy] = -0.5*(D[ix,iy]+D[ix+1,iy])*(S[ix+1,iy]-S[ix,iy])/dx  end
            if (2<=ix<=nx-1 && iy<=ny-1)  qHy[ix,iy] = -0.5*(D[ix,iy]+D[ix,iy+1])*(S[ix,iy+1]-S[ix,iy])/dy  end
        end
    end
    return
end
# [...] skipped lines
compute_flux!(qHx, qHy, S, D, dx, dy, nx, ny)
# [...] skipped lines
```
> 💡 Julia enables multi-threading capabilities by simply adding `Threads.@threads` to the outermost loop (here over `iy`).

3. The last step is to replace the (multi-threaded) loops by a vectorised GPU index
```julia
ix = (blockIdx().x-1) * blockDim().x + threadIdx().x
iy = (blockIdx().y-1) * blockDim().y + threadIdx().y
```
 specific to GPU execution. Each `ix` and `iy` are executed concurrently on a different GPU thread:
```julia
using CUDA
# [...] skipped lines
compute_flux!(qHx, qHy, S, D, dx, dy, nx, ny)
    ix = (blockIdx().x-1) * blockDim().x + threadIdx().x
    iy = (blockIdx().y-1) * blockDim().y + threadIdx().y
    if (ix<=nx-1 && 2<=iy<=ny-1)  qHx[ix,iy] = -0.5*(D[ix,iy]+D[ix+1,iy])*(S[ix+1,iy]-S[ix,iy])/dx  end
    if (2<=ix<=nx-1 && iy<=ny-1)  qHy[ix,iy] = -0.5*(D[ix,iy]+D[ix,iy+1])*(S[ix,iy+1]-S[ix,iy])/dy  end
    return
end
# [...] skipped lines
@cuda blocks=cublocks threads=cuthreads compute_flux!(qHx, qHy, S, D, dx, dy, nx, ny)
synchronize()
# [...] skipped lines
```
> 💡 We use `@cuda blocks=cublocks threads=cuthreads` to launch the GPU function on the appropriate number of threads, i.e. "parallel workers". The numerical grid resolution `nx` and `ny` must now be chosen accordingly to the number of parallel workers.

The here described porting procedure steps are exemplified for the 1D diffusion equation ([Porting the diffusion equation to GPUs](#porting-the-diffusion-equation-to-gpus)) available in the [extra material](#extras):
- (1) [`diffusion_1D_damp.jl`](scripts/diffusion_1D_damp.jl)
- (2) [`diffusion_1D_damp_fun.jl`](extras/diffusion_1D_damp_fun.jl)
- (3) [`diffusion_1D_damp_gpu.jl`](extras/diffusion_1D_damp_gpu.jl)
- (4) [`diffusion_1D_damp_xpu.jl`](scripts/diffusion_1D_damp_xpu.jl)

### XPU SIA implementation
Wouldn't it be great to be able to combine the multi-thread CPU and GPU implementations into a single "XPU" code to be able to run on various hardware with only changing a `USE_GPU` switch ? Using [ParallelStencil.jl] enables this, as many more cool features. The [`iceflow_xpu.jl`](scripts/iceflow_xpu.jl) script uses [ParallelStencil.jl] for an XPU implementation on various backends:

```julia
const USE_GPU = false
using ParallelStencil
using ParallelStencil.FiniteDifferences2D
@static if USE_GPU
    @init_parallel_stencil(CUDA, Float64, 2)
else
    @init_parallel_stencil(Threads, Float64, 2)
end
# [...] skipped lines
@parallel function compute_flux!(qHx, qHy, D, S, dx, dy)
    @all(qHx)  = -@av_ya(D)*@d_xi(S)/dx
    @all(qHy)  = -@av_xa(D)*@d_yi(S)/dy
    return
end
# [...] skipped lines
@parallel compute_flux!(qHx, qHy, D, S, dx, dy)
# [...] skipped lines
```
> 💡 Various macros `@(...)` permit to deal with the low-level technicalities and the `USE_GPU` flag enables to switch between CPU and GPU backend.

The resulting code is short and readable and solves the "two-language problem"; development and production code implementations are regrouped into a single code.

#### GPU - CPU Performance
The figure below depicts the time 1000 iterations (or pseudo-time steps) take running the [`iceflow_xpu.jl`](scripts/iceflow_xpu.jl) code on:
- an Intel Xeon Gold 6244 (4 threads - cores)
- an Nvidia RTX 2070 GPU

![](docs/timing.png)

⤴ [_back to course material_](#short-course-material)

### Greenland's ice cap evolution
We can finally use our XPU ice flow solver to simulate the evolution of Greenland's ice cap for a specific climate scenario. The steps are following:
1. Compute the steady-state ice thickness for current climate forcing (mass balance function as described in [SIA equation](#sia-equation) section)
2. Apply a time-dependent linear increase of the ELA (the equilibrium line where accumulation = ablation) of 1m/yr over the next 2500yrs. We here assume that an increase in annual mean temperature of 0.6°C is equivalent to a 100m shift of the ELA, thus 1m/yr represents 0.3°C per 50yrs. The [`iceflow_xpu_evo.jl`](scripts/iceflow_xpu_evo.jl) code implements this climate evolution model and produces the following predictions for the Greenland ice cap evolution:

![](docs/iceflow_evo_160x288.gif)

> Note that our climate scenario may reflect some extreme warming over the next 2500yrs.

⤴ [_back to course material_](#short-course-material)

# Extras
> ⚠ Due to the time limitation, the short course will not cover the [extras](#extras) material. This information complements the course's main content and may provide intermediate and/or additional development steps.

Extra material comprises:
* [Porting the diffusion equation to GPUs](#porting-the-diffusion-equation-to-gpus)
* [Simple inversion](#simple-inversion)
* [Performance metric](performance-metric)
* [Multi-XPU implementation](#multi-xpu-implementation)

⤴ [_back to content_](#content)

## Porting the diffusion equation to GPUs
This extra example complements the [GPU SIA implementation](#gpu-sia-implementation) and the [XPU SIA implementation](#xpu-sia-implementation).

### Parallel GPU computing
Let's start from the [`diffusion_1D_damp.jl`](scripts/diffusion_1D_damp.jl) code, a cool iterative and implicit solver in about 30 lines of code 🎉, and port it to GPU (with some intermediate steps).

1. Extract the physics calculations from [`diffusion_1D_damp.jl`](scripts/diffusion_1D_damp.jl), i.e. the time loop:
```julia
# [...] skipped lines
qH         .= -D*diff(H)/dx              # flux
ResH       .= -(H[2:end-1] - Hold[2:end-1])/dt - diff(qH)/dx # residual of the PDE
dHdtau     .= ResH + damp*dHdtau         # damped rate of change
H[2:end-1] .= H[2:end-1] + dtau*dHdtau   # update rule, sets the BC as H[1]=H[end]=0
# [...] skipped lines
```

2. Split the calculations into separate functions (or kernels) and call those functions within the time loop. The [`diffusion_1D_damp_fun.jl`](extras/diffusion_1D_damp_fun.jl) implements those modifications:
```julia
function compute_flux!(qH, H, D, dx, nx)
    Threads.@threads for ix=1:nx
        if (ix<=nx-1)  qH[ix] = -D*(H[ix+1]-H[ix])/dx  end
    end
    return
end

function compute_rate!(ResH, dHdt, H, Hold, qH, dt, damp, dx, nx)
    Threads.@threads for ix=1:nx
        if (2<=ix<=nx-1)  ResH[ix-1] = -(H[ix] - Hold[ix])/dt -(qH[ix]-qH[ix-1])/dx  end
        if (2<=ix<=nx-1)  dHdt[ix-1] = ResH[ix-1] + damp*dHdt[ix-1]  end
    end
    return
end

function compute_update!(H, dHdt, dtau, nx)
    Threads.@threads for ix=1:nx
        if (2<=ix<=nx-1)  H[ix] = H[ix] + dtau*dHdt[ix-1]  end
    end
    return
end
# [...] skipped lines
compute_flux!(qH, H, D, dx, nx)
compute_rate!(ResH, dHdt, H, Hold, qH, dt, damp, dx, nx)
compute_update!(H, dHdt, dtau, nx)
# [...] skipped lines
```
> 💡 Julia enables multi-threading capabilities by simply adding `Threads.@threads` to the outermost loop (here over `ix`).

3. The last step is to replace the (multi-threaded) loop by a vectorised index `ix = (blockIdx().x-1) * blockDim().x + threadIdx().x` specific to GPU execution. Each `ix` is executed concurrently on a different GPU thread. The [`diffusion_1D_damp_gpu.jl`](extras/diffusion_1D_damp_gpu.jl) implements those modifications to run on GPUs:
```julia
using CUDA
# [...] skipped lines
function compute_flux!(qH, H, D, dx, nx)
    ix = (blockIdx().x-1) * blockDim().x + threadIdx().x
    if (ix<=nx-1)  qH[ix] = -D*(H[ix+1]-H[ix])/dx  end
    return
end

function compute_rate!(ResH, dHdt, H, Hold, qH, dt, damp, dx, nx)
    ix = (blockIdx().x-1) * blockDim().x + threadIdx().x
    if (2<=ix<=nx-1)  ResH[ix-1] = -(H[ix] - Hold[ix])/dt -(qH[ix]-qH[ix-1])/dx  end
    if (2<=ix<=nx-1)  dHdt[ix-1] = ResH[ix-1] + damp*dHdt[ix-1]  end
    return
end

function compute_update!(H, dHdt, dtau, nx)
    ix = (blockIdx().x-1) * blockDim().x + threadIdx().x
    if (2<=ix<=nx-1)  H[ix] = H[ix] + dtau*dHdt[ix-1]  end
    return
end
# [...] skipped lines
@cuda blocks=cublocks threads=cuthreads compute_flux!(qH, H, D, dx, nx)
synchronize()
@cuda blocks=cublocks threads=cuthreads compute_rate!(ResH, dHdt, H, Hold, qH, dt, damp, dx, nx)
synchronize()
@cuda blocks=cublocks threads=cuthreads compute_update!(H, dHdt, dtau, nx)
synchronize()
# [...] skipped lines
```
> 💡 We use `@cuda blocks=cublocks threads=cuthreads` to launch the GPU function on the appropriate number of threads, i.e. "parallel workers". The numerical grid resolution `nx` must now be chosen accordingly to the number of workers `nx=cublocks*cuthreads`.

### XPU computing
Wouldn't it be great to be able to combine the multi-thread CPU and GPU implementations into a single "XPU" code to be able to run on various hardware with only changing a `USE_GPU` switch ? Using [ParallelStencil.jl] enables this, as well more other cool features. The [`diffusion_1D_damp_xpu.jl`](scripts/diffusion_1D_damp_xpu.jl) uses [ParallelStencil.jl] for an XPU implementation on various backends:

```julia
const USE_GPU = false
using ParallelStencil
using ParallelStencil.FiniteDifferences1D
@static if USE_GPU
    @init_parallel_stencil(CUDA, Float64, 1)
else
    @init_parallel_stencil(Threads, Float64, 1)
end
# [...] skipped lines
@parallel function compute_flux!(qH, H, D, dx)
    @all(qH) = -D*@d(H)/dx
    return
end

@parallel function compute_rate!(ResH, dHdt, H, Hold, qH, dt, damp, dx)
    @all(ResH) = -(@inn(H) - @inn(Hold))/dt -@d(qH)/dx
    @all(dHdt) = @all(ResH) + damp*@all(dHdt)
    return
end

@parallel function compute_update!(H, dHdt, dtau)
    @inn(H) = @inn(H) + dtau*@all(dHdt)
    return
end
# [...] skipped lines
@parallel compute_flux!(qH, H, D, dx)
@parallel compute_rate!(ResH, dHdt, H, Hold, qH, dt, damp, dx)
@parallel compute_update!(H, dHdt, dtau)
# [...] skipped lines
```
Various macros `@(...)` permit to deal with the low-level technicalities and the `USE_GPU` flag enables to switch between CPU and GPU backend.

⤴ [_back to extras_](#extras)

## Simple inversion
Using the inversion approach proposed by \[[7][Visnjevic18]\], our ice flow solver [`iceflow.jl`](scripts/iceflow.jl) can be embedded into an inversion framework to retrieve spatially variable maximum accumulation rate `b_max` in order to constrain ice thickness distribution over Greenland. The following animation depicts the evolution of the inversion procedure as function of the 30 inversion steps and was produced using the [`iceflow_inverse.jl`](extras/iceflow_inverse.jl) code. `Gam` represents the misfit between the observed `Hice` and the calculated `H` ice thickness, `B_max` represents the spatially variable maximal accumulation.

![](docs/iceflow_inv_160x304.gif)

The ice thickness obtained from the inversion procedure can be further compared to the [BedMachine Greenland v3] ice thickness data:

![](docs/iceflow_inv_out2_160x304.png)

> 💡 Note that the inversion procedure serves here as proof of concept, as higher resolution and finer tuning may be needed to further improve the misfit minimisation.

⤴ [_back to extras_](#extras)

## Performance metric
Majority of stencil based codes as in this course are memory bounded, meaning the limiting factor in performance is the rate at which memory is transferred from and back between the memory and the arithmetic units. The maximal rate at which the memory transfers occur is the memory copy rate, in the order of 50 GB/s for CPUs and about 1 TB/s for modern GPUs. The effective memory throughput metric (T_eff) measures how good an iterative stencil-based algorithm performs in terms of memory throughput, to be compared to the memory copy rate.

Check out the [performance metric section](https://github.com/omlins/ParallelStencil.jl#performance-metric) from the [ParallelStencil.jl] module and this [JuliaCon2020][JuliaCon20a] presentation \[[1][JuliaCon20a]\].

⤴ [_back to extras_](#extras)

## Multi-XPU implementation
Check out [this material](https://github.com/luraess/geo-hpc-course#running-julia-mpi) to figure out how combining [ImplicitGlobalGrid.jl] to [ParallelStencil.jl] enables efficient distributed memory parallelisation on multiple XPUs.

⤴ [_back to extras_](#extras)

# Further reading
\[1\] [Omlin, S., Räss, L., Kwasniewski, G., Malvoisin, B., & Podladchikov, Y. Y. (2020). Solving Nonlinear Multi-Physics on GPU Supercomputers with Julia. JuliaCon Conference, virtual.][JuliaCon20a]

\[2\] [Räss, L., Reuber, G., & Omlin, S. (2020). Multi-Physics 3-D Inversion on GPU Supercomputers with Julia. JuliaCon Conference, virtual.][JuliaCon20b]

\[3\] [Räss, L., Omlin, S., & Podladchikov, Y. Y. (2019). Porting a Massively Parallel Multi-GPU Application to Julia: a 3-D Nonlinear Multi-Physics Flow Solver. JuliaCon Conference, Baltimore, USA.][JuliaCon19]

\[4\] [Frankel, S. P. (1950). Convergence rates of iterative treatments of partial differential equations, Mathe. Tables Other Aids Comput., 4, 65–75.][Frankel50]

\[5\] [Machgut, H. et al. (2016). Greenland surface mass-balance observations from the ice-sheet ablation area and local glaciers. Journal of Glaciology, 62(235), 861-887.][Machgut16]

\[6\] [Jarosch, A. H., Schoof, C. G., & Anslow, F. S. (2013). Restoring mass conservation to shallow ice flow models over complex terrain, The Cryosphere, 7, 229–240.][Jarosch13]

\[7\] [Visnjevic, V., Herman, F., & Podladchikov, Y. (2018). Reconstructing spatially variable mass balances from past ice extents by inverse modeling. Journal of Glaciology, 64(248), 957-968.][Visnjevic18]

⤴ [_back to content_](#content)

[Julia]: https://julialang.org
[Julia language]: https://docs.julialang.org/en/v1/
[Julia REPL]: https://docs.julialang.org/en/v1/stdlib/REPL/
[Base.Threads]: https://docs.julialang.org/en/v1/base/multi-threading/
[JULIA_NUM_THREADS]:https://docs.julialang.org/en/v1.0.0/manual/environment-variables/#JULIA_NUM_THREADS-1
[CUDA.jl]: https://github.com/JuliaGPU/CUDA.jl
[JuliaGPU]: https://juliagpu.org
[ParallelStencil.jl]: https://github.com/omlins/ParallelStencil.jl
[ImplicitGlobalGrid.jl]: https://github.com/eth-cscs/ImplicitGlobalGrid.jl

[BedMachine Greenland v3]: https://sites.uci.edu/morlighem/dataproducts/bedmachine-greenland/

[JuliaCon20a]: https://www.youtube.com/watch?v=vPsfZUqI4_0
[JuliaCon20b]: https://www.youtube.com/watch?v=1t1AKnnGRqA
[JuliaCon19]: https://www.youtube.com/watch?v=b90qqbYJ58Q
[Frankel50]: https://doi.org/10.2307/2002770
[Machgut16]: https://doi.org/10.1017/jog.2016.75
[Jarosch13]: https://doi.org/10.5194/tc-7-229-2013
[Visnjevic18]: https://doi.org/10.1017/jog.2018.82

gha_license_id: MIT


text:
# djoser

[![Build Status](https://travis-ci.org/sunscrapers/djoser.svg?branch=master)](https://travis-ci.org/sunscrapers/djoser)
[![Coverage Status](https://coveralls.io/repos/sunscrapers/djoser/badge.png?branch=master)](https://coveralls.io/r/sunscrapers/djoser?branch=master)

REST implementation of [Django](https://www.djangoproject.com/) authentication
system. **Djoser** library provides a set of [Django Rest Framework](http://www.django-rest-framework.org/)
views to handle basic actions such as registration, login, logout, password
reset and account activation. It works with [custom user model](https://docs.djangoproject.com/en/dev/topics/auth/customizing/).

Instead of reusing Django code (e.g. `PasswordResetForm`), we reimplemented
few things to fit better into [Single Page App](http://en.wikipedia.org/wiki/Single-page_application)
architecture.

Developed by [SUNSCRAPERS](http://sunscrapers.com/) with passion & patience.

## Features

Here is a list of supported authentication backends:

 * [HTTP Basic Auth](http://www.django-rest-framework.org/api-guide/authentication/#basicauthentication) (Default)
 * [Token based authentication from Django Rest Framework](http://www.django-rest-framework.org/api-guide/authentication#tokenauthentication)

Available endpoints:

 * `/me/`
 * `/register/`
 * `/login/` (token based authentication)
 * `/logout/` (token based authentication)
 * `/activate/`
 * `/{{ User.USERNAME_FIELD }}/`
 * `/password/`
 * `/password/reset/`
 * `/password/reset/confirm/`

Supported Python versions:

 * Python 2.7
 * Python 3.4
 * Python 3.5

Supported Django versions:

 * Django 1.7
 * Django 1.8
 * Django 1.9
 * Django 1.10

Supported Django Rest Framework versions:

 * Django Rest Framework 3.x

For Django Rest Framework 2.4 support check [djoser 0.3.2](https://github.com/sunscrapers/djoser/tree/0.3.2).

## Installation

Use `pip`:

    $ pip install djoser

## Quick Start

Configure `INSTALLED_APPS`:

```python
INSTALLED_APPS = (
    'django.contrib.auth',
    (...),
    'rest_framework',
    'djoser',
    (...),
)
```

Configure `urls.py`:

```python
urlpatterns = patterns('',
    (...),
    url(r'^auth/', include('djoser.urls')),
)
```

HTTP Basic Auth strategy is assumed by default as Django Rest Framework does it. However you may want to set it
explicitly:

```python
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
    ),
}
```

Run migrations - this step will create tables for `auth` app:

    $ ./manage.py migrate

## Customizing authentication backend

### Token Based Authentication

Add `'rest_framework.authtoken'` to `INSTALLED_APPS`:

```python
INSTALLED_APPS = (
    'django.contrib.auth',
    (...),
    'rest_framework',
    'rest_framework.authtoken',
    'djoser',
    (...),
)
```

Configure `urls.py`. Pay attention to `djoser.url.authtoken` module path.

```python
urlpatterns = patterns('',
    (...),
    url(r'^auth/', include('djoser.urls.authtoken')),
)
```

Set `TokenAuthentication` as default Django Rest Framework authentication strategy:

```python
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
}
```

Run migrations - this step will create tables for `auth` and `authtoken` apps:

    $ ./manage.py migrate

### JSON Web Token Authentication
`djoser` does not provide support for JSON web token authentication out of the box but
can be enabled by using a library like [djangorestframework-jwt](https://github.com/GetBlimp/django-rest-framework-jwt).

You simply need to route correctly in your `settings.ROOT_URLCONF`. An example would be:

```
import rest_framework_jwt.views
import djoser.views

urlpatterns = [
    url(r'^auth/login', rest_framework_jwt.views.obtain_jwt_token),  # using JSON web token
    url(r'^auth/register', djoser.views.RegistrationView.as_view()),
    url(r'^auth/password/reset', djoser.views.PasswordResetView.as_view()),
    url(r'^auth/password/reset/confirm', djoser.views.PasswordResetConfirmView.as_view()),
    ...
]
```

## Settings

Optionally add `DJOSER` settings:

```python
DJOSER = {
    'DOMAIN': 'frontend.com',
    'SITE_NAME': 'Frontend',
    'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
    'ACTIVATION_URL': '#/activate/{uid}/{token}',
    'SEND_ACTIVATION_EMAIL': True,
    'PASSWORD_VALIDATORS': [],
    'SERIALIZERS': {},
}
```

Check "Settings" section for more info.

## Endpoints

### User

Use this endpoint to retrieve/update user.

#### `GET`

URL: `/me/`

Retrieve user.

* **response**

    * status: `HTTP_200_OK` (success)

    * data:

        `{{ User.USERNAME_FIELD }}`

        `{{ User._meta.pk.name }}`

        `{{ User.REQUIRED_FIELDS }}`

#### `PUT`

URL: `/me/`

Update user.

* **request**

    * data:

        `{{ User.REQUIRED_FIELDS }}`

* **response**

    * status: `HTTP_200_OK` (success)

    * data:

        `{{ User.USERNAME_FIELD }}`

        `{{ User._meta.pk.name }}`

        `{{ User.REQUIRED_FIELDS }}`

### Register

Use this endpoint to register new user. Your user model manager should
implement [create_user](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.UserManager.create_user)
method and have [USERNAME_FIELD](https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.CustomUser.USERNAME_FIELD)
and [REQUIRED_FIELDS](https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.CustomUser.REQUIRED_FIELDS)
fields.

#### `POST`

URL: `/register/`

* **request**

    * data:

        `{{ User.USERNAME_FIELD }}`

        `{{ User.REQUIRED_FIELDS }}`

        `password`

* **response**

    * status: `HTTP_201_CREATED` (success)

    * data:

        `{{ User.USERNAME_FIELD }}`

        `{{ User._meta.pk.name }}`

        `{{ User.REQUIRED_FIELDS }}`

### Login

Use this endpoint to obtain user [authentication token](http://www.django-rest-framework.org/api-guide/authentication#tokenauthentication).
This endpoint is available only if you are using token based authentication.

#### `POST`

URL: `/login/`

* **request**

    * data:

        `{{ User.USERNAME_FIELD }}`

        `password`

* **response**

    * status: `HTTP_200_OK` (success)

    * data:

        `auth_token`

### Logout

Use this endpoint to logout user (remove user authentication token). This endpoint is available only if you are using
token based authentication.

#### `POST`

URL: `/logout/`

* **response**

    * status: `HTTP_204_NO_CONTENT` (success)

### Activate

Use this endpoint to activate user account. This endpoint is not a URL which
will be directly exposed to your users - you should provide site in your
frontend application (configured by `ACTIVATION_URL`) which will send `POST`
request to activate endpoint.

#### `POST`

URL: `/activate/`

* **request**

    * data:

        `uid`

        `token`

* **response**

    * status: `HTTP_204_NO_CONTENT` (success)

### Set username

Use this endpoint to change user username (`USERNAME_FIELD`).

#### `POST`

URL: `/{{ User.USERNAME_FIELD }}/`

* **request**

    * data:

        `new_{{ User.USERNAME_FIELD }}`

        `re_new_{{ User.USERNAME_FIELD }}` (if `SET_USERNAME_RETYPE` is `True`)

        `current_password`

* **response**

    * status: `HTTP_204_NO_CONTENT` (success)

### Set password

Use this endpoint to change user password.

#### `POST`

URL: `/password/`

* **request**

    * data:

        `new_password`

        `re_new_password` (if `SET_PASSWORD_RETYPE` is `True`)

        `current_password`

* **response**

    * status: `HTTP_204_NO_CONTENT` (success)

### Reset password

Use this endpoint to send email to user with password reset link. You have to
setup `PASSWORD_RESET_CONFIRM_URL`.

#### `POST`

URL: `/password/reset/`

* **request**

    * data:

        `email`

* **response**

    * status: `HTTP_204_NO_CONTENT` (success), if `PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND` is `False` (default); or
    * status: `HTTP_400_BAD_REQUEST`, if `PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND` is `True` and `email` does not exists in the database.

### Reset password confirmation

Use this endpoint to finish reset password process. This endpoint is not a URL
which will be directly exposed to your users - you should provide site in your
frontend application (configured by `PASSWORD_RESET_CONFIRM_URL`) which
will send `POST` request to reset password confirmation endpoint.

#### `POST`

URL: `/password/reset/confirm/`

* **request**

    * data:

        `uid`

        `token`

        `new_password`

        `re_new_password` (if `PASSWORD_RESET_CONFIRM_RETYPE` is `True`)

* **response**

    * status: `HTTP_204_NO_CONTENT` (success)

## Settings

### DOMAIN

Domain of your frontend app. If not provided, domain of current site will be
used.

**Required**: `False`

### SITE_NAME

Name of your frontend app. If not provided, name of current site will be
used.

**Required**: `False`

### PASSWORD_RESET_CONFIRM_URL

URL to your frontend password reset page. It should contain `{uid}` and
`{token}` placeholders, e.g. `#/password-reset/{uid}/{token}`. You should pass
`uid` and `token` to reset password confirmation endpoint.

**Required**: `True`

### SEND_ACTIVATION_EMAIL

If `True`, register endpoint will send activation email to user.

**Default**: `False`

### SEND_CONFIRMATION_EMAIL

If `True`, register or activation endpoint will send confirmation email to user.

**Default**: `False`

### ACTIVATION_URL

URL to your frontend activation page. It should contain `{uid}` and `{token}`
placeholders, e.g. `#/activate/{uid}/{token}`. You should pass `uid` and
`token` to activation endpoint.

**Required**: `True`

### SET_USERNAME_RETYPE

If `True`, you need to pass `re_new_{{ User.USERNAME_FIELD }}` to
`/{{ User.USERNAME_FIELD }}/` endpoint, to validate username equality.

**Default**: `False`

### SET_PASSWORD_RETYPE

If `True`, you need to pass `re_new_password` to `/password/` endpoint, to
validate password equality.

**Default**: `False`

### PASSWORD_RESET_CONFIRM_RETYPE

If `True`, you need to pass `re_new_password` to `/password/reset/confirm/`
endpoint in order to validate password equality.

**Default**: `False`

### LOGOUT_ON_PASSWORD_CHANGE

If `True`, setting new password will logout the user.

**Default**: `False`

### PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND

If `True`, posting a non-existent `email` to `/password/reset/` will return
a `HTTP_400_BAD_REQUEST` response with an `EMAIL_NOT_FOUND` error message
('User with given email does not exist.').

If `False` (default), the `/password/reset/` endpoint will always return
a `HTTP_204_NO_CONTENT` response.

Please note that setting this to `True` will expose information whether
an email is registered in the system.

**Default**: `False`

### PASSWORD_VALIDATORS

List containing [REST Framework Validator](http://www.django-rest-framework.org/api-guide/validators/) functions.
These validators are run on `/register/` and `/password/reset/confirm/`.

**Default**: `[]`

**Example**: `[my_validator1, my_validator2]`

### SERIALIZERS

This dictionary is used to update the defaults, so by providing,
let's say, one key, all the others will still be used.

**Examples**
```
{
    'user': 'myapp.serializers.SpecialUserSerializer',
}
```

**Default**:
```
{
    'activation': 'djoser.serializers.ActivationSerializer',
    'login': 'djoser.serializers.LoginSerializer',
    'password_reset': 'djoser.serializers.PasswordResetSerializer',
    'password_reset_confirm': 'djoser.serializers.PasswordResetConfirmSerializer',
    'password_reset_confirm_retype': 'djoser.serializers.PasswordResetConfirmRetypeSerializer',
    'set_password': 'djoser.serializers.SetPasswordSerializer',
    'set_password_retype': 'djoser.serializers.SetPasswordRetypeSerializer',
    'set_username': 'djoser.serializers.SetUsernameSerializer',
    'set_username_retype': 'djoser.serializers.SetUsernameRetypeSerializer',
    'user_registration': 'djoser.serializers.UserRegistrationSerializer',
    'user': 'djoser.serializers.UserSerializer',
    'token': 'djoser.serializers.TokenSerializer',
}
```

## Emails

There are few email templates which you may want to override:

* `activation_email_body.txt`
* `activation_email_subject.txt`
* `password_reset_email_body.txt`
* `password_reset_email_subject.txt`

All of them have following context:

* `user`
* `domain`
* `site_name`
* `url`
* `uid`
* `token`
* `protocol`

## Sample usage

We provide a standalone test app for you to start easily, see how everything works with basic settings. It might be useful before integrating **djoser** into your backend application.

In this extremely short tutorial we are going to mimic the simplest flow: register user, log in and log out. We will also check resource access on each consecutive step. Let's go!

* Clone repository and install **djoser** to your virtualenv:

    `$ git clone git@github.com:sunscrapers/djoser.git`

    `$ cd djoser`

    `$ pip install -e .`

* Go to the `testproject` directory, migrate the database and start the development server:

    `$ cd testproject`

    `$ ./manage.py migrate`

    `$ ./manage.py runserver 8088`

* Register a new user:

    `$ curl -X POST http://<IP_ADDRESS>:8088/auth/register/ --data 'username=djoser&password=djoser'`

    `{"email": "", "username": "djoser", "id":1}`

    So far, so good. We have just created a new user using REST API.

* Let's access user's details:

    `$ curl -X GET http://<IP_ADDRESS>:8088/auth/me/`

    `{"detail": "Authentication credentials were not provided."}`

    As we can see, we cannot access user profile without logging in. Pretty obvious.

* Let's log in:

    `curl -X POST http://<IP_ADDRESS>:8088/auth/login/ --data 'username=djoser&password=djoser'`

    `{"auth_token": "b704c9fc3655635646356ac2950269f352ea1139"}`

    We have just obtained an authorization token that we may use later in order to retrieve specific resources.

* Let's access user's details again:

    `$ curl -X GET http://<IP_ADDRESS>:8088/auth/me/`

    `{"detail": "Authentication credentials were not provided."}`

    Access is still forbidden but let's offer the token we obtained:

    `$ curl -X GET http://<IP_ADDRESS>:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`

    `{"email": "", "username": "djoser", "id": 1}`

    Yay, it works!

* Now let's log out:

    `curl -X POST http://<IP_ADDRESS>:8088/auth/logout/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`

    And try access user profile again:

    `$ curl -X GET http://<IP_ADDRESS>:8088/auth/me/ -H 'Authorization: Token b704c9fc3655635646356ac2950269f352ea1139'`

    `{"detail": "Invalid token"}`

    As we can see, user has been logged out successfully and the proper token has been removed.

## Customization

If you need to customize any serializer behaviour you can use
the DJOSER['SERIALIZERS'] setting to use your own serializer classes in the built-in views.
Or if you need to completely change the default djoser behaviour,
you can always override djoser views with your own custom ones.

Define custom `urls` instead of reusing `djoser.urls`:

```python
urlpatterns = patterns('',
    (...),
    url(r'^register/$', views.CustomRegistrationView.as_view()),
)
```

Define custom view/serializer (inherit from one of `djoser` class) and override necessary method/field:

```python
class CustomRegistrationView(djoser.views.RegistrationView):

    def send_activation_email(self, *args, **kwargs):
        your_custom_email_sender(*args, **kwargs)
```

You could check `djoser` API in source code:

* [djoser.views](https://github.com/sunscrapers/djoser/blob/master/djoser/views.py)
* [djoser.serializers](https://github.com/sunscrapers/djoser/blob/master/djoser/serializers.py)

## Contributing and development

To start developing on **djoser**, clone the repository:

`$ git clone git@github.com:sunscrapers/djoser.git`

In order to run the tests create virtualenv, go to repo directory and then:

`$ pip install django djangorestframework`

`$ pip install -r requirements.txt`

If you are running djoser tests on Python 2.7 you also need to install `mock` library.

`$ pip install mock  # only on Python 2.7`

`$ cd testproject`

`$ ./manage.py test`

If you need to run tests against all supported Python and Django versions then invoke:

`$ pip install tox`

`$ tox`

You can also play with test project by running following commands:

`$ ./manage.py migrate`

`$ ./manage.py runserver`

## Similar projects

List of projects related to Django, REST and authentication:

- [django-rest-auth](https://github.com/Tivix/django-rest-auth)
- [django-rest-framework-digestauth](https://github.com/juanriaza/django-rest-framework-digestauth)
- [django-oauth-toolkit](https://github.com/evonove/django-oauth-toolkit)
- [doac](https://github.com/Rediker-Software/doac)
- [django-rest-framework-jwt](https://github.com/GetBlimp/django-rest-framework-jwt)
- [django-rest-framework-httpsignature](https://github.com/etoccalino/django-rest-framework-httpsignature)
- [hawkrest](https://github.com/kumar303/hawkrest)

license: MIT

repository: oliver-zhou/djoser

branch_name: refs/heads/master

extension: md

license_type: permissive

repo_name: oliver-zhou/djoser


text:
# Geographical Plotting with Python Part 3
## - More customization
Here we cover even more of the many options to consider with basemap's mapping abilities. We still haven't even gotten to plotting yet! Don't worry, we will get there soon, I promise. In this series, you are taught how to plot on a map and do geographical plotting with the matplotlib extension called basemap. -sentdex published on Sep 29, 2013.

## Step -1-
We will add some other features to our current basemap, and to get
```
m.bluemarble() // is to draw the marble shape map.
```
here is the output, this one you can zoom into it,
![]()
Zoom into Japan we get:

## Authors

* **Ghasak Ibrahim** - *Initial work* -

## License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
## Acknowledgments
* Hat tip to anyone whose code was used

## Inspiration
following this project from:
My Original thoughts.

## To add some keyboard keys use:
Ctrl
## Adding more features:
## Requirements
python 0.x

Packages: see **requirements.txt**

## Instructions
1. Install all required packages
2. Modify parameters if desired
3. Run **folder/script.R**

gha_language: Python


text:
# docker-node
---
Sample application to demonstrate debugging a Node application running within a Docker container

clone:

    `git clone https://github.com/serdar/docker-node.git`

build:

    `docker build . -t docker-node`

run:

    `docker run -p 90:3000 -p 9229:9229 --name nomnom  docker-node`

Launch.json file in VS Code to attach (create a new "project" to run this in):

```
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "port": 9229,
            "name": "Attach to Node (Docker)",
            "protocol": "inspector",
            "remoteRoot": "/app",
            "skipFiles": [
                "<node_internals>/**"
            ]
        }
    ]
}
```

repository: serdar/docker-node

gha_language: JavaScript

repo_name: serdar/docker-node


text:
Snap Challenge: Sample Interview Questions # 1
In pseudocode, write a function that takes an array of numbers, and returns another array that counts the number of times each number appears in the array.

Use Array.forEach

1. Const the array of numbers.
2. create a variable
3. create a for loop (example for (i=0; i < array.length; i++)) to run a function on the length of the array.
4. if the array brings back a number that is seen only once, console log that number
5. else, if the number is seen more than once, return the occurence of that number

In pseudocode, write a function that takes two integers. For each integer in between those two numbers (inclusive), output 'fizz' if the number is even, 'buzz' if the number is divisible by 3, and 'baz' without 'fizz' or 'buzz' if the number is divisible by both. Otherwise, output the number.

Given this css, draw a box model of the div.

div {
width: 250px;
height: 110px;
margin-top: 10px;
padding: 20px;
}

repository: gabriela-reyes/snap-challenge

branch_name: refs/heads/main

repo_name: gabriela-reyes/snap-challenge


text:
# Delaunays Dream
## Description
TBA
## Required Installation(s):
Python: https://www.python.org/downloads

## Dependecies:
PyQt - User Interface

PyQt Designer - Designer Application for PyQt

Opencv - Image and Video processing

### Installation:

    pip install PyQt5
    pip install PyQt5Designer
    pip install opencv-contrib-python

### Converting .ui to .py:

    pyuic5 -x input_filename.ui -o output_filename.py

### Run application:

    python out_filename.py

## Author
Octavio Ochoa

repository: chensation/Delaunay-Dream

repo_name: chensation/Delaunay-Dream


text:
# [Double assignment of local variable](https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#SA_LOCAL_DOUBLE_ASSIGNMENT)

 This method contains a double assignment of a local variable; e.g.

    public void foo() {
        int x,y;
        x = x = 17;
    }

Assigning the same value to a variable twice is useless, and may indicate a logic error or typo.

repository: codacy/codacy-spotbugs

gha_language: Scala

gha_license_id: NOASSERTION

repo_name: codacy/codacy-spotbugs


text:
# plotly.js image testing

Test plotly.js with the Plotly Image-Server docker container.

### Run the container

Inside your `plotly.js` directory, run

```bash
docker run -d --name imagetest \
    -v $PWD:/var/www/streambed/image_server/plotly.js \
    -p 9010:9010 -p 2022:22 plotly/imageserver:[version]
```

where `[version]` is the latest Plotly Image-Server docker container version
as listed on
[hub.docker.com](https://hub.docker.com/r/plotly/imageserver/tags/) and
`imagetest` is the name of the docker container.

### Run the tests

Inside your `plotly.js` directory, run

```bash
npm run test-image
```

### SSH into docker

```bash
ssh -p 2022 root@localhost # with password `root`
```

If you got this error:

```
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
dd:1e:e0:95:8d:ef:06:b8:0f:2f.
Please contact your system administrator.
Add correct host key in /home/jh/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/jh/.ssh/known_hosts:104
  remove with: ssh-keygen -f "/home/jh/.ssh/known_hosts" -R [localhost]:2022
ECDSA host key for [localhost]:2022 has changed and you have requested strict checking.
Host key verification failed.
```
simply run

```bash
ssh-keygen -f "${HOME}/.ssh/known_hosts" -R [localhost]:2022
```

to remove host information.

### List all images

```bash
docker images
```

### List all containers

```bash
docker ps -a
```

### Stop container

```bash
docker stop [container_id]
```

### Remove container

```bash
docker rm [container_id]
```

For more comprehensive information about docker, please refer to [docker document](http://docs.docker.com/)


text:
# Exploring MIDI Files with MidiFile

The `MidiFile` class in NAudio allows you to open and examine the MIDI events in a standard MIDI file. It can also be used to create or update MIDI files, but this article focuses on reading.

## Opening a MIDI file

Opening a `MidiFile` is as simple as creating a new `MidiFile` object and passing in the path. You can choose to enable `strictMode` which will throw exceptions if various faults are found with the file such as note on events missing a paired note off or controller values out of range.

```c#
var strictMode = false;
var mf = new MidiFile(fileName, strictMode);
```

We can discover what MIDI file format the file is (Type 0 or type 1), as well as how many tracks are present and what the `DeltaTicksPerQuarterNote` value is.

```c#
Console.WriteLine("Format {0}, Tracks {1}, Delta Ticks Per Quarter Note {2}",
                mf.FileFormat, mf.Tracks, mf.DeltaTicksPerQuarterNote);
```

## Examining the MIDI events

The MIDI events can be accessed with the `Events` property, passing in the index of the track whose events you want to access. This gives you a `MidiEventCollection` you can iterate through.

All the events in the MIDI file will be represented by a class inheriting from `MidiEvent`. The `MidiFile` class will also have set an `AbsoluteTime` property on each note, which represents the timestamp of the MIDI event from the start of file in terms of delta ticks.

For note on events, `MidiFile` will also try to pair up the corresponding `NoteOffEvent` events. This allows you to see the duration of each note (which is simply the difference in time between the absolute time of the `NoteOffEvent` and `NoteOnEvent`.

Each `MidiEvent` has a `ToString` overload with basic information, so we can print out details of all the events in the file like this. (we don't print out the `NoteOffEvent` instances, because they are each paired to a `NoteOnEvent` which reports the duration)

```c#
for (int n = 0; n < mf.Tracks; n++)
{
    foreach (var midiEvent in mf.Events[n])
    {
        if(!MidiEvent.IsNoteOff(midiEvent))
        {
            Console.WriteLine("{0} {1}\r\n", ToMBT(midiEvent.AbsoluteTime, mf.DeltaTicksPerQuarterNote, timeSignature), midiEvent);
        }
    }
}
```

You'll see that a helper `ToMBT` method is being used above to convert the `AbsoluteTime` into a more helpful Measures Beats Ticks format. Here's a basic implementation (that doesn't take into account any possible time signature events that might take place)

```c#
private string ToMBT(long eventTime, int ticksPerQuarterNote, TimeSignatureEvent timeSignature)
{
    int beatsPerBar = timeSignature == null ? 4 : timeSignature.Numerator;
    int ticksPerBar = timeSignature == null ? ticksPerQuarterNote * 4 : (timeSignature.Numerator * ticksPerQuarterNote * 4) / (1 << timeSignature.Denominator);
    int ticksPerBeat = ticksPerBar / beatsPerBar;
    long bar = 1 + (eventTime / ticksPerBar);
    long beat = 1 + ((eventTime % ticksPerBar) / ticksPerBeat);
    long tick = eventTime % ticksPerBeat;
    return String.Format("{0}:{1}:{2}", bar, beat, tick);
}
```

Note that to get the `TimeSignatureEvent` needed by this function we can simply do something like:

```c#
var timeSignature = mf.Events[0].OfType<TimeSignatureEvent>().FirstOrDefault();
```

gha_language: C#


text:
# MaterialishProgress-Xamarin
Xamarin bindings for [materialish-progress](https://github.com/pnikosis/materialish-progress) by [@pnikosis](https://github.com/pnikosis)

A material style progress wheel compatible with 2.3

This project keeps a consistent look in an app across all Android Versions. The progress wheel is quite cool in Lollipop, and pretty horrible on Gingerbread.

This view uses [Progress Wheel](https://github.com/Todd-Davies/ProgressWheel) as a base, but has been almost completely rewritten (the original view uses handlers for updating the wheel).

This implementation tries to follow as close as possible the guidelines for the circular progress as [described here](http://www.google.com/design/spec/components/progress-activity.html#progress-activity-types-of-indicators).

## License
[MIT License](https://github.com/akamud/MaterialishProgress-Xamarin/blob/master/LICENSE)

repository: akamud/MaterialishProgress-Xamarin

repo_name: akamud/MaterialishProgress-Xamarin


text:
# Anticuado

This library collect __outdated__ libraries for each platforms. [Here](https://github.com/KazuCocoa/anticuado-example) is example how to use this library.
And [this travis project](https://travis-ci.org/KazuCocoa/anticuado-example) is just sample how to run this and the output.

## Support
- Java
    - Gradle
- iOS
    - CocoaPods
    - Carthage
- Ruby
    - Bundler
- Elixir
    - Hex
- JavaScript
    - npm

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'anticuado'
```

And then execute:

    $ bundle

Or install it yourself as:

    $ gem install anticuado

## Usage

This library's results are like the following format:

```ruby
[
  {
    library_name: "AFNetworking",
    current_version: "2.5.0",
    available_version: "3.1.0",
    latest_version: "3.1.0"
  },
  {
    library_name: "OHHTTPStubs",
    current_version: "4.1.0",
    available_version: "5.0.0",
    latest_version: "5.0.0"
  }
]
```

### iOS
#### CocoaPods
1. install `cocoadpos`. Read [official document](https://cocoapods.org/).
2. Run as the following script.

```ruby
require "anticuado"

cocoadpos = ::Anticuado::IOS::CocoaPods.new "path/to/project"
outdated = cocoadpos.outdated
cocoadpos.format outdated
```

#### Carthage
1. install `carthage`. Read [official document](https://github.com/Carthage/Carthage).
2. Run as the following script.

```ruby
require "anticuado"

carthage = ::Anticuado::IOS::Carthage.new "path/to/project"
outdated = carthage.outdated
carthage.format outdated
```

### Android
#### Gradle

This library require the following plugin to collect library versions.
https://github.com/ben-manes/gradle-versions-plugin

1. install `gradle` or set gradle wrapper, `gradlew`. Read [official document](https://gradle.org/).
2. Run as the following script.

```ruby
require "anticuado"

gradle = ::Anticuado::Java::Gradle.new "path/to/project"
gradle.outdated # Writes the result to "build/dependencyUpdates" by defaylt
outdated = gradle.parse_json "build/dependencyUpdates"
gradle.format outdated
```

### Elixir
#### Hex
1. install `mix`. Read [official document](http://elixir-lang.org/install.html).
2. Run as the following script.

```ruby
require "anticuado"

hex = ::Anticuado::Elixir::Hex.new "path/to/project"
outdated = hex.outdated
hex.format outdated
```

### Ruby
#### Bundler
1. install `bundler`. Read [official document](http://bundler.io/).
2. Run as the following script.

```ruby
require "anticuado"

bundler = ::Anticuado::Ruby::Bundler.new "path/to/project"
outdated = bundler.outdated
bundler.format outdated
```

## In advance
- Create a PR automatically to update libraries
    - See test/anticuado/git_test.rb

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Kazu_cocoa/anticuado. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).

repository: KazuCocoa/anticuado

gha_language: Ruby

repo_name: KazuCocoa/anticuado


text:
# Employee's directory
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Contents
* [Overview](#Overview)
* [License](#License)
* [Contributing](#Contributing)
* [Demo](#Demo)
* [Questions](#Questions)
* [Go to live app](https://jlcalderon.github.io/employees-directory/)

## Overview
This repository contains a React app that renders a container with employees and sort and filters them by interacting with the components. On the background the app is contolling the employees with state variables (``` search, results, resultsTmp ```). To get a sample of employees data this app uses the [*randomuser.me/*](https://randomuser.me/). This app uses 3 components (``` Wrapper, Form, UsersContainer```)

## License
This is an open source app. This app is cover under the **MIT** licences terms. For more information open this link to see the app licence terms of use [*Licence*](https://opensource.org/licenses/MIT)

## Contributing
If you would like to contribute to this small app, feel free to do so. Please know that any contribution will be check and verify before to merging to the production app environment main branch. You can `fork` and `clone` this repository make your changes `push to your branch` and make a `pull request` to compare your code and the main branch across forks and put your changes/additions/corrections on this app.oject.

## Demo
![Demo of this app](Employees-Directory-React-App.gif)

## Questions
Any further question about this project email direct to<EMAIL_ADDRESS>feel free to reach out and follow me on [Github](https://github.com/jlcalderon).

repository: jlcalderon/employees-directory

repo_name: jlcalderon/employees-directory


text:
Introduction
==

This project provides a platform for training a Neural Network
on classification of words based on their etymology.
The Network can be trained interactively from command line or from a simple GUI
or using a small dataset that distinguishes words between those of
Greek and non Greek origin.

Next we discuss the motivation the challenges associated with this problem,
then the Design Decisions and finally how a classifier can be
trained.

Motivation
==
Etymology of words, if known, can help us learn a
language faster and understand it better.
Learning any etymology is a big undertaking so
_Word Etymologist_ partially fulfills this promise.
For the time being it can be trained on recognising
English words of Greek origin from a limited dataset
or trained interactively to perform custom classification.

An ideal fully featured solution should consist of a web service
in which the user can do the following.
- Contribute data for classification
- Train different Neural Network models with data contributed by community
or by themselves.
- Use already trained models for prediction.

Challenges
==

Large number of roots
--
The different etymological roots
is so large that is hard to enumerate them entirely.
It is also likely that for many roots we can
find very few words that will make it hard for any
model to be trained reliably on those roots.
My guess is that given enough data we can identify
a set of the most common roots and then we can train
a classifier to identify only those.
Another interesting question to this end is
whether Zipf's law applies in this setting,
since it has proven to describe well data
frequencies in similar applications.

Words with multiple meanings
--
There are a lot of words with multiple
meanings and likely different etymologies.
An example is `trunk` which has 4 meanings and
2 etymologies according to
[etymonline dictionary](https://www.etymonline.com/word/trunk).

Contested/Unknown etymologies
--
A lot of etymologies are based on assumptions
and are hard to verify by a non specialist
and often there is no single widely
accepted etymology.

Invented words
--
Words are invented all the time and there is no
easy way to track the origin of newly invented words.

Design Decisions
==

Decisions for automated training
--

I chose to create an English word dataset
with each word being annotated based on whether
its origin is Greek or not.
This simplifies classification since it is binary
and I can verify etymology claims I look up
due to my knowledge of Greek (modern and ancient).
Since it is possible that a word is combined from
a Greek and non Greek word I annotate each letter
separately, while for words that their Greek origin
is contested I have an intermediate classification
(0 non Greek, 1 contested Greek, 2 Greek).
An example of a word I consider contested is `air`
just because it is also considered a Latin root
and there are words with the prefix
`aero-` in English which is "more" Greek.

It is also unlikely that a word has two meanings
one with a greek root and another one without.
The only example I came up with is `pan`
which actually is a prefix of Greek origin
rather than a word.

Training picks random words from dataset to
train an LSTM Neural Network.
Currently the classifier treats any non 0 annotation
as Greek.
Input takes a small window of word characters
and classifies the first character in the window.
Words are padded with spaces to classify every
character in the word.
Context matters so the window size
`W` should be large enough such that it
is possible to classify the first letter of a word
by looking at the first `W` characters.

It is expected that if features of a Greek word
are picked up from the Network, then even made up words
that combine existing Greek words will be successfully
classified.

Decisions for interactive training
--
Interactive training works with Bidirectional LSTMs.
For each different length of words a different network
needs to be created.
Number of parameters are independent of the length of
a word so parameters are loaded from one model to another
after they are trained on an input.
Since the training is interactive,
this does not delay training in any significant way.

Other Decisions
--
Models and their parameters are saved to root folder
and with default names that depend on the
number of hidden layers of the model and window size.

In other words naming is by convention rather than
configuration, but until custom naming is supported
an alternative is to backup/rename trained files
of models that you want to train from scratch.

Run Training
==

To automatically train a NN first uncompress `words.csv` file
from `words.zip` compressed file and then run

    python train.py --program 1

To evaluate a previously trained NN run

    python train.py --program 2

To train a NN interactively run

    python train.py --program 3

**[Recommended]**
To train a NN interactively with a GUI run

    python train.py --program 4

For more options run

    python train.py --help

Expected to work on Python 3.6 or later

Contributing
==

Check the `CONTRIBUTING.md` file.


text:
# Basic HTTP Server written in C++
## About
I've made it to learn about sockets and HTTP protocol itself.
For now, it's very vulnerable and offers support for a small number of file types, but I want to enhance this project and maybe add some kind of middleware in the future.

[Library used to manage sockets](https://github.com/ReneNyffenegger/Socket.cpp)

## Usage
First, compile using Makefile.
Then, run like a normal console program. Enter the port you want to use as an argument.
Put your files in public_html folder.


text:
Contributing
============

Help in development is always welcome!  Whether you have a bugfix, new
features, new ideas or just want to correct a typo in some documentation, the
easiest way to track its progress is by opening a tracking issue.

In case you already have working code - even better. Simply link the pull
request in the issue. If you have not done so already, you _may_ also add
yourself to the [list of constributors][contributors] (that's up to You, there
is no need to so).

Please respect that I maintain this on my own currently and have limited time.
I appreciate suggestions but sometimes the associate workload can seem
daunting. That means that simplifications to the workflow are also *highly*
appreciated. I may also not get back to you immediately.

By contributing code, you agree to make it available under both MIT license and
Apache-2.0 license at the choice of the licensee and assure that you have the
rights to do so (the Github terms of service also require this from You).

This project generally follows a **full disclosure principle**.  We believe
that vulnerabilities are fixed most swiftly when shared openly. As this project
is mainly a library and not end-user software, it also enables other developers
to react in an informed manner.  Should you nevertheless believe this method to
be inappropriate, you may discuss bugs over [encrypted e-mail][pgp-key].

[contributors]: ../Contributors.txt
[pgp-key]: http://pgp.mit.edu/pks/lookup?op=vindex&search=0x8BFB6B35887B56B8

repository: HeroicKatora/oxide-auth

gha_language: Rust

repo_name: HeroicKatora/oxide-auth


text:
# epha-robot

robot is a tool (entirely written in [node.js](https://nodejs.org/)) for fetching, purifying and transforming pharmaceutical (csv, xlsx, xml, zip) into machine readable (JSON, csv) data.

robot uses public resources like [swissmedic - Product information](http://download.swissmedicinfo.ch/Accept.aspx?Lang=EN) and is meant to be as a starting point for studies, thesis and further processing and purifying of the data.

## Table of contents

- [Jobs](#jobs) - [Benefits](#benefits) - [Install](#install-robot) - [Usage](#usage) - [Development](#development)
  - [atc](#atc)
  - [bag](#bag)
  - [kompendium](#kompendium)
  - [swissmedic](#swissmedic)

## Benefits

- reliable and smart fetching of pharmaceutical data
- auto-transformation into JSON-files: for example from xlsx-files
- supports the following data/sources:
  - [ATC - Official ATC-Index (WidO)](http://wido.de/amtl_atc-code.html)
  - [BAG - Federal Office of Public Health FOPH](http://www.spezialitaetenliste.ch/)
  - [swissmedic - Human and veterinary medicines](https://www.swissmedic.ch/arzneimittel/00156/00221/00222/00230/index.html?lang=en)
  - [kompendium - Pharmaceutical Product Information](http://download.swissmedicinfo.ch/Accept.aspx?ReturnUrl=%2f)

## Jobs

### atc

This job generates a map of *Anatomical Therapeutic Chemical Classification System*-data.

**Start:**

1) go to robot location and type `npm start`

```shell
npm start

><EMAIL_ADDRESS>start /Your/robot/location/
> node ./bin/cli.js

EMIL: I'm ready, if you are? Type help for help.
```
2) after prompt is ready type **atc**

```shell
> atc

EMIL: Added 'atc' to the queue (1 jobs)!
EMIL: You can run queue with 'go'
```
3) then type **go** to start queued job

```shell
> go
```

4) ... some logging ...

```shell
<EMAIL_ADDRESS>|  TIME |  ATC Completed in { duration: '15306ms' }
```

5) ... done!

**Downloads:**

  - **source:** [WIdO](http://wido.de/amtl_atc-code.html) - Wissenschaftliches Institut der AOK
  - `{PROCESS_ROOT}/data/auto/atcatc.zip` (> 4.5MB) containing atc.xlsx

**Releases:**

  - drive: `{PROCESS_ROOT}/data/release/atc`
  - `atc.csv`
  - `atc.json`
  - `atc.min.json`

**atc.json - Sample:**

```javascript
//..
   "A01AA51": {
      "name": "Natriumfluorid, Kombinationen"
   },
   "A01AB": {
      "name": "Antiinfektiva und Antiseptika zur oralen Lokalbehandlung"
   },
   "A01AB02": {
      "name": "Wasserstoffperoxid",
      "ddd": "60 mg O"
   },
//..
```

### bag

Gets a collection of pharmaceutical products containing purchase and selling price. There is also a history keeping track of all products (incl. de-registered products). Besides that the job also provides bi-temporal data for purchase and selling prices.

**Start:**

1) go to robot location and type `npm start`

```shell
npm start

><EMAIL_ADDRESS>start /Your/robot/location/
> node ./bin/cli.js

EMIL: I'm ready, if you are? Type help for help.
```
2) after prompt is ready type **bag**

```shell
> bag

EMIL: Added 'bag' to the queue (1 jobs)!
EMIL: You can run queue with 'go'
```
3) then type **go** to start queued job

```shell
> go
```

4) ... some logging ...

```shell
<EMAIL_ADDRESS>|  TIME |  BAG Completed in { duration: '28844ms' }
```

5) ... done!

**Downloads:**

- **source:** [BAG](http://www.spezialitaetenliste.ch/) - Bundesamt für Gesundheit (CH)
- drive: `{PROCESS_ROOT}/data/auto/bag/XMLPublications.zip` (~ 5MB) contains: `bag.xls`, `bag.xml`, `it.xml`

**Releases:**

- drive: `{PROCESS_ROOT}/data/release/bag/`
  - `bag.json`
  - `bag.min.json`
  - `bag.history.json`
  - `bag.history.min.json`
  - `bag.price-history.json`
  - `bag.price-history.min.json`
  - `it.json`
  - `it.min.json`

**bag.json - Sample:**

```javascript
// ...
   {
      "name": "3TC",
      "atc": "J05AF05",
      "description": "Filmtabl 150 mg",
      "orgGenCode": "O",
      "flagSB20": "N",
      "vatInEXF": "N",
      "substances": [
         {
            "name": "Lamivudinum",
            "quantity": "150",
            "quantityUnit": "mg"
         }
      ],
      "packung": "60 Stk",
      "flagNarcosis": "N",
      "bagDossier": "16577",
      "gtin": "7680536620137",
      "exFactoryPreis": "164.55",
      "exFactoryPreisValid": "01.10.2011",
      "publikumsPreis": "205.30",
      "publikumsPreisValid": "01.10.2011",
      "validFrom": "15.03.1996"
   },
//...
```

#### bag-history(-job)
In `bag.history.json` the job keeps automatically track of de-registered products and price changes. This file will be automatically created after the first run (at this moment contents will be equal to `bag.history.json`). Deleting this file is the same as restarting the history. Probably it is necessary - especially bevor un-installing/removing robot - to backup this file from time to time.

**bag.history.json - Sample:**

```javascript
      //...
      "publikumsPreisHistory": [
         // history-entity
         {
            "dateTime": "08.06.2015 17:09", // time of change
            "publikumsPreis": [
               "205.30", //before
               "300.00"  //after
            ],
            "publikumsPreisValid": [
               "01.10.2011", //before
               "08.06.2015" //after
            ]
         }
         // ..
      ]
      //...
```

#### bag-price-history
**robot** records in `bag.price-history.json` product price changes (purchase and selling price). Each run of the job will update this file if a change was detected.
Products are identified by their GTIN.

Usually prices rarely change. So dates at `validFrom` and `validTo` are on day basis. Date is formatted like in `bag.json`: `DD.MM.YYYY`. Please note: `validFrom` is including while `validTo` is excluding.

There are two types of prices:

- `exFactory`: purchase price
- `publikum`: selling price

and two sub-types:

- `valid`: time for a price valid in the real world
- `transaction`: time for a price detected by robot

`valid` and `transaction` are collections and latest price may be found at `index 0`. `validTo` is `null` or rather `Infinite` for most recent price as this information is not available.

**bag.price-history.json - Sample:**

```javascript
{
   "7680536620137": [
      {
         "exFactoryPreis": "196.35",
         "publikumsPreis": "214.99",
         "validFrom": "18.06.2015",
         "validTo": null,
         "transactionFrom": "18.06.2015", // recorded by robot
         "transactionTo": null
      },
      {
         "exFactoryPreis": "176.45",
         "publikumsPreis": "209.99",
         "validFrom": "11.01.2011", // parsed from data
         "validTo": "17.06.2015",
         "transactionFrom": "01.01.2015", // recorded by robot
         "transactionTo": "17.06.2015"
      }
   ],
}
```

#### bag-logs

Additionally to the history-file, logs for *new*, *changed* and *de-registered* products will be written:

- drive: `{PROCESS_ROOT}/logs/bag/`
  - `bag.changes.log`
  - `bag.new.log`
  - `bag.de-registered.log`

It could be very handy to use `tail -f` on this logs.

### kompendium:
The **kompendium**-job fetches a huge catalog of pharmaceutical product information and is also quite time and resource consuming. The downloaded file itself has around 190MB (> 800MB unzipped). The job will also build a huge amount of .htm-files (~25000) containing product specific and patient related information in German, French and Italian (if available).

**Start:**

1) go to robot location and type `npm start`

```shell
npm start

><EMAIL_ADDRESS>start /Your/robot/location/
> node ./bin/cli.js

EMIL: I'm ready, if you are? Type help for help.
```
2) after prompt is ready type **kompendium**

```shell
> kompendium

EMIL: Added 'kompendium' to the queue (1 jobs)!
EMIL: You can run queue with 'go'
```
3) then type **go** to start queued job

```shell
> go
```

4) ... some logging ...

```shell
<EMAIL_ADDRESS>|  TIME |  Kompendium Completed in { duration: '299261ms' }
```

5) ... done!

**Downloads**

- **source:** [Swissmedic](http://download.swissmedicinfo.ch/Accept.aspx?ReturnUrl=%2f) - Swiss Agency for Therapeutic Products
- drive: `{PROCESS_ROOT}/data/auto/kompendium/kompendium.zip` (190MB)
- containing `kompendium.xml` (~850MB)

**Releases**

- drive:
  - `{PROCESS_ROOT}/data/release/kompendium`
     - `kompendium.json`
     - `kompendium.min.json`
     - `catalog.json`
  - German FI/PI: `{PROCESS_ROOT}/data/release/kompendium/de/`
     - `fi/{REGISTRATION_NUMMBER}.htm`
     - `pi/{REGISTRATION_NUMMBER}.htm`
  - French FI/PI: `{PROCESS_ROOT}/data/release/kompendium/fr/`
     - `fi/{REGISTRATION_NUMMBER}.htm`
     - `pi/{REGISTRATION_NUMMBER}.htm`
  - Italian FI/PI: `{PROCESS_ROOT}/data/release/kompendium/it/`
     - `fi/{REGISTRATION_NUMMBER}.htm`
     - `pi/{REGISTRATION_NUMMBER}.htm`

**kompendium.json - Sample:**

```javascript
{
  "documents": [
    // ...
    {
      "zulassung": "10167",
      "lang": "de fr it",
      "type": "fi pi",
      "produkt": "Emser Salz®",
      "substanz": "Emser Salz",
      "hersteller": "Sidroga AG",
      "atc": "RO2AX",
      "files": [
        //language/type/{REGISTRATION_NUMMBER}.htm
        "de/fi/10167.htm",
        "fr/fi/10167.htm",
        "de/pi/10167.htm",
        "fr/pi/10167.htm",
        "it/pi/10167.htm"
      ]
    }
    // ...
  ]
}
```

### swissmedic:
This job fetches data about human and veterinary medicines. It also creates a history-file and triggers the **atc**-Job if required.

#### atc/CH
When there is no **atc**-Release available it auto-runs the **atc**-Job as it is a dependency for **atcCH**. *Please note* that if there is an **atc**-Release available it will use it. This release could be potentially *out-of-date*. So it is up to the user to run **atc**-Job if necessary.

#### swissmedicHistory(-job)
There will be also a `swissmedic.history.json` which keeps track of de-registered products. This file will be automatically created after the first run (at that moment contents will be equal to `swissmedic.json`). Deleting this file is the same as restarting the history. De-registered products will be flagged with `{ "deregistered": "DD.MM.YYYY" }`. Please note: Before re-installing robot it is advisible to backup this file.

#### swissmedic-logs
Like **bag** there will be logs for *new*, *changed* and *de-registered* products:

- drive: `{PROCESS_ROOT}/logs/swissmedic/`
  - `swissmedic.changes.log`
  - `swissmedic.new.log`
  - `swissmedic.de-registered.log`

Think of `tail -f`, it might be useful.

**Start:**

1) go to robot location and type `npm start`

```shell
npm start

><EMAIL_ADDRESS>start /Your/robot/location/
> node ./bin/cli.js

EMIL: I'm ready, if you are? Type help for help.
```
2) after prompt is ready type **swissmedic**

```shell
> swissmedic

EMIL: Added 'swissmedic' to the queue (1 jobs)!
EMIL: You can run queue with 'go'
```
3) then type **go** to start queued job

```shell
> go
```

4) ... some logging ...

```shell
<EMAIL_ADDRESS>|  TIME |  Swissmedic Completed in { duration: '13369ms' }
```

5) ... done!

**Downloads:**

- **source:** [Swissmedic](https://www.swissmedic.ch/arzneimittel/00156/00221/00222/00230/index.html?lang=en) - Swiss Agency for Therapeutic Products

- swissmedic:
  - location: `{PROCESS_ROOT}/data/auto/swissmedic/`
  - `swissmedic.xlsx` (> 2.5MB)

- atc (as a side effect)
  - location: `{PROCESS_ROOT}/data/release/atc`
  - `atc.zip` (> 4.5MB), containing `atc.xlsx`

**Releases:**

- atc
  - location: `{PROCESS_ROOT}/data/release/atc`
  - `atc_de-ch.json`
  - `atc_de-ch.min.json`
  - as a side effect
     - (`atc.csv`)
     - (`atc.json`)
     - (`atc.min.json`)

- swissmedic:
  - location: `{PROCESS_ROOT}/data/release/swissmedic/`
     - `swissmedic.json`
     - `swissmedic.min.json`
     - `swissmedic.history.json`
     - `swissmedic.history.min.json`

**swissmedic.json - Sample**

```javascript
//..
   {
      "zulassung": "00277",
      "sequenz": "1",
      "name": "Coeur-Vaisseaux Sérocytol, suppositoire",
      "hersteller": "Sérolab, société anonyme",
      "itnummer": "08.07.",
      "atc": "J06AA",
      "heilmittelcode": "Blutprodukte",
      "erstzulassung": "26.4.2010",
      "zulassungsdatum": "26.4.2010",
      "gueltigkeitsdatum": "25.4.2020",
      "verpackung": "001",
      "packungsgroesse": "3",
      "einheit": "Suppositorien",
      "abgabekategorie": "B",
      "wirkstoffe": "globulina equina (immunisé avec coeur, endothélium vasculaire porcins)",
      "zusammensetzung": "globulina equina (immunisé avec coeur, endothélium vasculaire porcins) 8 mg, propylenglycolum, conserv.: E 216, E 218, excipiens pro suppositorio.",
      "anwendungsgebiet": "Traitement immunomodulant selon le Dr Thomas\r\n\r\nPossibilités d'emploi voir information professionnelle",
      "gtin": "7680002770014"
   },
//..
```

## Install robot

### Requirements

- node.js >= v0.12.x ([Installing information]((https://github.com/joyent/node/wiki/Installation)))
- npm > 2.7.x (usually shipped with node.js)

### Installation
#### npm

```shell
npm install epha-robot
```
#### github
```
cd path/to/your/WORKSPACE
git clone https://github.com/epha/robot.git
cd robot
npm install
```
## Usage

### CLI
```bash
npm start

><EMAIL_ADDRESS>start
> node ./bin/cli.js
EMIL: I'm ready, if you are? Type help for help.
> help
EMIL: You can add jobs to the queue e.g.
EMIL: 'atc' << Codes & DDD
EMIL: 'bag' << Spezialitätenliste
EMIL: 'kompendium' << Swissmedic Kompendium
EMIL: 'swissmedic' << Registered products CH
EMIL: and then run queue with 'go'
EMIL: I'm ready, if you are? Type help for help.
>
```

### npm scripts

#### robot-service

```shell
npm run robot-service
```

Probably the most common use-case for robot: runs **outdated** each 30 minutes (default). It is possible to adjust re-run-time by passing `DELAY={OTHER_VALUE}` (milliseconds). `DELAY` should depend on your internet connection and cpu power.

**Example:**

`DELAY=60000 npm run robot-service` will run *outdated* **every hour**.

Will only exit manually or when it crashes. Log level is reduced to *warnings* and *errors*.
It could be quite useful running the underlying script (`bin/outdated`) with a daemon like [forever](https://www.npmjs.com/package/forever), [pm2](https://www.npmjs.com/package/pm2) so that it will automatically restart if it crashes (which shouldn't happen).

**stdout - sample:**

```shell
<EMAIL_ADDRESS>|  WARN |  robot-service 12.06.2015 08:36 - Start Outdated Check
<EMAIL_ADDRESS>|  WARN |  BAG File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  ATC File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  Swissmedic File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  Kompendium File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  robot-service 12.06.2015 08:36 - Finished Outdated Check
<EMAIL_ADDRESS>|  WARN |  robot-service 12.06.2015 09:06 - Start Outdated Check
<EMAIL_ADDRESS>|  WARN |  BAG File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  ATC File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  Swissmedic File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  Kompendium File on disk is up-to-date
<EMAIL_ADDRESS>|  WARN |  robot-service 12.06.2015 09:06 - Finished Outdated Check
```

#### start

```shell
npm start
```

Starts the robot-[cli](#cli).

#### all

```shell
npm run all
```

Runs all jobs (atc, bag, kompendium, swissmedic) in parallel. Useful with a broadband internet connection and powerful cpu to get as fast as possible the current state. Exists when done or fails. Will overwrite/updates existing files.

#### outdated
```shell
npm run outdated
```
Checks sources on changes by header-`content-length`. If `content-length` diffs to file-size on disk it will trigger appropriate job. Runs jobs in sequence and exits when is done or fails.

### Programmatical

```javascript
var robot = require("epha-robot");
var disk = require("epha-robot").common.disk;
var kompendiumJob = robot.kompendium;
var kompendiumCfg = robot.kompendium.cfg;

kompendiumJob()
    .then(function () {
      return disk.read.json(kompendiumCfg.process.file);
    })
    .then(function (data) {
      // do something with data
    })
    .catch(function (err) {
      console.error("OH NO!", err.message, err.stack);
    });
```

## Development

### job-configs

Each job has it's own config file. However there is a convention for configs:

```javascript
/* any config file*/

// Will resolve paths according to {PROCESS_ROOT}
var config = require("lib/common/config");

module.exports = config("anyJobName", {
  "download": {
    "url": "..."
    "linkParser": /RegExp/i
    "zipFiles": [{name: /RegExpForFileInZip/, dest: }]
  },
  //optional
  "manual": {

  },
  "release": {
    "file": "anyJobName.json" // will resolve to {PROCESS_ROOT}/data/anyJobName/release/anyJobName.json
    "minFile": "anyJobName.min.json",
    "nested": {
      "file": nested.json //will also resolve to full path
    }
  },
  //optional
  "history": {
    "file": "anyJobName.history.json",
    "minFile": "anyJobName.history.min.json"
  },
  //optional
  "log": {
    "deRegistered": "anyJobName.de-registered.log",
    "changes": "anyJobName",
    "new": "anyJobName"
  }
});
```

### creating a history file

Basically it should be possible to create for each release a history-file by using `history`-lib if

- the release is a JSON-collection.
- each collection entry has a key `gtin` that identifies this entry

```javascript
/* history job for anyJob */

var history = require("lib/history/history");

var cfg = require("jobs/cfg/anyJobCfg");

/**
 * Pass a logger if default-logger doesn't fit with your desired log-level, but it is optional
 * History returns a Promise.
 */
function anyJobHistory(log) {

  // will be called if a change was detected.
  // passes references to currently processed history- and newData entry
  function onChanged(diff, historyData, newData) {
    // do something fancy, a good example mighty be jobs/bagHistory.js
  }

  // cfg must contain information about where to put history- and log-files
  // @see job.configs
  return history("anyJob", cfg, onChanged, log);
}

module.exports = anyJobHistory;

```

### working with files

robot ships with `lib/common/disk.js` which allows comfortable working with files through a Promise-based-API. This example should give an idea of what it can do:

```javascript

var path = require("path");
var disk = require("lib/common/disk");
var bagJob = require("jobs/bag");
var bagCfg = require("jobs/cfg/bag.cfg)";
var processBAGData = require("lib/processBAGData");

function workWithFiles() {
  return new Promise(function (resolve, reject) {
    disk
      .fileExists(cfg.download.file)
      .then(function (fileExists) {
        if (fileExists && path.extname(cfg.download.file) === ".zip") {
          // zipFiles is an array with information about files which should be unzipped
          return disk
            .unzip(cfg.download.zipFiles)
            .then(function () {
              return disk.read.file(cfg.download.zipFiles[0].name)
            })
            .then(function (unzippedFileData) {
              return processBAGData(unzippedFileData);
            })
            .then(function (processedData) {
              return Promise.all([disk.write.json("myFile.json"), disk.write.jsonMin("myFile.min.json")]);
            })
            .catch(reject);
        }
        // else run bag-job first
        bagJob().then(workWithFiles);
      })
      .catch(reject);
  });
}

```

### Tests

#### Unit-Tests

- `npm test`: Runs the unit-tests once
- `npm run watch-test`: Watches project's files and re-runs unit-tests on change
- both support growling. check [tj/node-growl](https://github.com/tj/node-growl) to enable it on your machine.

#### Integration-Tests

`npm run test-integration`

  1. Run `npm run init-test-integration` which will download fresh data to `{ROOT}/data/auto` & `{ROOT}/data/release` and copies it to `{ROOT}/fixtures` to use it as fixtures
  2. Spins up a real node http-server that serves atc, swissmedic etc. dummy sites and downloads.
  3. It runs each job against the integration-testing-server and tests the whole flow from html parsing to creating release files.


text:
# pubsub

## Introduction

These are a series of example modules to demonstrate use of `espace'
as a platform for publisher/subscriber services.

Each module implements the same functionality, however, the modules
with higher suffix number have more features.

The intention here is to demonstrate, and experiment with, various
techniques for developing `espace' based applications.

## Build

`rebar3` is used for compilation and testing.

```
rebar3 compile
rebar3 dialyzer
rebar3 edoc
```

## Trying out the demo

### pubsub_0

The `demo_0:start/0` function will run a demonstration of `pubsub_0`
steps listed below.

* from the pubsub directory, start the shell

```
$ rebar3 shell
> demo_0:start().
```

The demo will generate `logger` messages when running. The following
events will take place at one second intervals:

* The `pubsub` service will be started and initialized with three
  subscribers, `alpha`, `beta` and `gamma`.
* Two messages will be published. We expect the two messages to be
  reported by all three subscribers.
* Another subscriber, `delta`, will be added to the group. The last
  message will be reported by `delta`.
* One last message will be published. All four subscribers will report
  the message.
* The service will be stopped.

## pubsub_1

This is a slightly more sophisticated version of `pubsub_0`. Use
`pubsub_1:demo_1/0` to run the demo.

Enjoy!

Fred

repository: fredyouhanaie/espace-examples

repo_name: fredyouhanaie/espace-examples


text:
# Topic

> 機器學習與人類偏見 (Machine Learning and Human Bias)

> But just because something is based on data doesn't automatically make it neutral. Even with good intentions, it's impossible to separate ourselves from our own human biases, so our human biases become part of the technology we create in many different ways.

> 但我們不能理所當然地因為它是基於數據資料，就認為機器學習很中立。即使立意良好，我們永遠也無法剝除自我偏見，所以在許多方面，人類偏見變成我們科技發明的一部份。

## Host
Host: Priscilla

Today issue: Is there bias in machine learning?

## learning points
1. _
	* be based on  [bes]  (ph.)  以…為基礎
		- Your assumption is based on the fact that the man is not lying.
			+ 你的假設是基於這個男人不撒謊的事實。
	* on the basis of  (ph.)  以…為基礎
		- be based on <=> on the basis of 可互相代換, 注意順序,  A is based on B = B is on the basis of A
		- On the basis of the results, we have no choice but to discard our original hypothesis.
			+ 在結果的基礎上，我們別無選擇，只能拋棄我們原來的假設。
	* basic  [ˋbesɪk]  (adj.)  基礎的；基本的
		- The poor lack the basic necessities of life.
			+ 窮人缺乏基本的生活必需品。
	* basically  [ˋbesɪk!ɪ]  (adv.)  基本上
		- Basically, I despise her for her hypocritical character.
			+ 基本上，我鄙視她的虛偽性格。

2. _
	* automatically  [͵ɔtəˋmætɪk!ɪ]  (adv.)  自動地；無意識地
		- I want to buy those locks which automatically lock the doors when we close them.
			+ 我想購買這個鎖定開關，以便在關閉它們時自動鎖定門。
	* automatic  [͵ɔtəˋmætɪk]  (adj.)  自動的、自動裝置的
		- I got this fully automatic washing machine.
			+ 我有這台全自動洗衣機。
	* auto-  [ˋɔto]  (prefix.)  自己的；自動的
		- auto => 字根, 自動化, auto focus camera => 自動對焦相機
	* atomic  [əˋtɑmɪk]  (adj.)  原子的
		- Atomic bombs are horrible weapons that have killed countless people.
			+ 原子彈是可怕的武器，已經殺死了無數人。
			+ Atomic bombs 原子彈

3. _
	* neutral  [ˋnjutrəl]  (adj.)  中立的；不偏不倚的
		- He failed to remain neutral during the debate.
			+ 他在辯論中未能保持中立。 <= 完全中立
	* objective  [əbˋdʒɛktɪv]  (adj.)  客觀的；無偏見的
		- A judge must give an objective opinion in court.
			+ 法官必須在法庭上提出客觀的意見。 <= 客觀的意見
	* subjective  [səbˋdʒɛktɪv]  (adj.)  主觀的
		- This is a very subjective judgment for her work performance.
			+ 這是對她工作表現的非常主觀的判斷。

4. _
	* bias  [ˋbaɪəs]  (n.)  偏見；成見
		- Some people have a bias against Asian people.
			+ 有些人對亞洲人有偏見。
	* biased  [ˋbaɪəst]  (adj.)  存有偏見的；偏見的
		- This report biased so it's not very convincing.
			+ 這份報告有偏見，所以它不太令人信服。
	* prejudice  [ˋprɛdʒədɪs]  (n.)  偏見、歧視
		- dice 發音注意
		- The history teacher has a prejudice in our favor.
			+ 歷史老師對我們較為偏心。
			+ prejudice in our favor => 偏心


text:
## Instructions

The aim of the kata is to decompose n! (factorial n) into its prime factors.

#### Examples:
```
n = 12; decomp(12) -> "2^10 * 3^5 * 5^2 * 7 * 11"
since 12! is divisible by 2 ten times, by 3 five times, by 5 two times and by 7 and 11 only once.

n = 22; decomp(22) -> "2^19 * 3^9 * 5^4 * 7^3 * 11^2 * 13 * 17 * 19"

n = 25; decomp(25) -> 2^22 * 3^10 * 5^6 * 7^3 * 11^2 * 13 * 17 * 19 * 23
```

Prime numbers should be in increasing order. When the exponent of a prime is 1
don't put the exponent.

#### Notes

- The function is decomp(n) and should return the decomposition of n! into its
prime factors in increasing order of the primes, as a string.
- Factorial can be a very big number (4000! has 12674 digits, n will go from
300 to 4000).
- In Fortran - as in any other language - the returned string is not permitted
to contain any redundant trailing whitespace: you can use dynamically allocated
character strings.


text:
Next Three Fiber

	🚧  Next Three Fiber 🚀 Under construction...  🚧

  About   |
  Technologies   |
  Requirements   |
  Starting   |
  License   |
  Author

## :dart: About

The purpose of this repository is for studies and practices with React three fiber applying to Next.js :smile:

## :rocket: Technologies

The following tools were used in this project:

- [React Three Fiber](https://github.com/pmndrs/react-three-fiber)
- [React Spring](https://www.react-spring.io/)
- [Three.js](https://threejs.org/)
- [React](https://pt-br.reactjs.org/)
- [TypeScript](https://www.typescriptlang.org/)

## :white_check_mark: Requirements

Before starting :checkered_flag:, you need to have [Git](https://git-scm.com), [Node](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/getting-started/install) installed.

## :checkered_flag: Starting

```bash
# Clone this project
$ git clone https://github.com/JohnAnon9771/next-three-fiber

# Access
$ cd next-three-fiber

# Install dependencies
$ yarn

# Run the project in development mode
$ yarn dev

# The server will initialize in the <http://localhost:3000>
```

## :memo: License

This project is under license from MIT. For more details, see the [LICENSE](LICENSE.md) file.

Made with :heart: by JohnAnon9771

Back to top

gha_language: TypeScript


text:
# Changelog

## [1.1.1](https://github.com/antidot-framework/antidot-cli/tree/HEAD)

[Full Changelog](https://github.com/antidot-framework/antidot-cli/compare/1.1.0...HEAD)

**Merged pull requests:**

- Update phpstan/phpstan requirement from ^0.11.5 to ^0.11.5 || ^0.12.0 [\#5](https://github.com/antidot-framework/antidot-cli/pull/5) ([dependabot[bot]](https://github.com/apps/dependabot))
- Update symfony/var-dumper requirement from ^4.2 to ^4.2 || ^5.0 [\#4](https://github.com/antidot-framework/antidot-cli/pull/4) ([dependabot[bot]](https://github.com/apps/dependabot))
- Update phpro/grumphp requirement from ^0.15.0 to ^0.15.0 || ^0.19.0 [\#3](https://github.com/antidot-framework/antidot-cli/pull/3) ([dependabot[bot]](https://github.com/apps/dependabot))
- Update phpunit/phpunit requirement from ^8.0 to ^8.0 || ^9.0 [\#2](https://github.com/antidot-framework/antidot-cli/pull/2) ([dependabot[bot]](https://github.com/apps/dependabot))

## [1.1.0](https://github.com/antidot-framework/antidot-cli/tree/1.1.0) (2020-06-01)

[Full Changelog](https://github.com/antidot-framework/antidot-cli/compare/1.0.0...1.1.0)

**Closed issues:**

- Document Cli component [\#1](https://github.com/antidot-framework/antidot-cli/issues/1)

## [1.0.0](https://github.com/antidot-framework/antidot-cli/tree/1.0.0) (2019-06-15)

[Full Changelog](https://github.com/antidot-framework/antidot-cli/compare/b526d40d90f049a242495960bd5ac174bdbfe5af...1.0.0)

\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*

repository: open-source-contributions/antidot-cli

repo_name: open-source-contributions/antidot-cli


text:
---
title: Incorrectly Correct
author: Jeremy
tags: [mathematics, education]
permalink: /146
date: 2019-07-17
---

![A student makes a mistake while simplifying sin(x) = sin(2) by "cancelling" the "sin".](https://res.cloudinary.com/dh3hm8pb7/image/upload/c_scale,q_auto:best/v1535842782/Handwaving/Published/IncorrectlyCorrect.png){: .centre-image }

If you don't handle this well, students will start thinking there's a conspiracy against them.