text:
# Reads a model file, which contains all parameters for simulating an object

"""
    read_model(file)

Read a model file. The model file holds the choice and the parameterization of the models.

# Arguments

- `file::String`: path to a model file

# Examples

```julia
models = read_model("path_to_a_model_file.yaml")
```
"""
function read_model(file)
    model = YAML.load_file(file; dicttype=OrderedDict{String,Any})

    !is_model(model) && error("model argument is not a model (e.g. as returned from `read_model()`)");
    group = model["Group"]
    types = collect(keys(model["Type"]))

    components = Dict{String,AbstractComponentModel}()

    for (i,j) in model["Type"]
        # i = "Leaf"
        # j = model["Type"][i]

        processes = OrderedDict{Symbol,Union{Missing,AbstractModel}}()
        for (k,l) in j
            # k = "Interception"
            # l = j[k]

            process = get_process(k)
            if !ismissing(process)
                # Checking if there are several models or just one given without the "use" keyword:
                if haskey(l,"model")
                    # Here we only have one model, and it is given as it is

                    #  Check if the user didn't mess up both approaches when writing:
                    haskey(l,"use") && error("Cannot use the name 'model' for a model name. ",
                    "Error happens in process `$k` of component type `$i`")

                    modelused = l
                elseif haskey(l,"use")
                    modelused = l[l["use"]]
                else
                    error("Error in parsing process `$k` of component type `$i`. Please check.")
                end

                model_process = get_model(pop!(modelused, "model"), process)
                model_process = instantiate(model_process,modelused)
                if !ismissing(model_process)
                    push!(processes, Symbol(process) => model_process)
                end
            end
        end

        sort!(processes)
        processes = (;processes...)

        # Get the component type based on the models used (*e.g.*, if photosynthetic, use `LeafModels`):
        componenttype = get_component_type(processes...)

        push!(components, i => componenttype(;processes...))
    end

    return components
end

"""
    get_component_type(processes)

Return the component type (the actual struct) given the processes passed as a named Tuple.
It is considered a `LeafModels` if it presents models for `photosynthesis` and
`stomatal_conductance`, and optionally for `interception` and `energy`.
"""
function get_component_type(::E,::I,::A,::Gs) where {E<:AbstractEnergyModel,
    I<:AbstractInterceptionModel,A<:AbstractAModel,Gs<:AbstractGsModel}

    return LeafModels
end

function get_component_type(::E,::A,::Gs) where {E<:AbstractEnergyModel,
    I<:AbstractInterceptionModel,A<:AbstractAModel,Gs<:AbstractGsModel}

    return LeafModels
end

function get_component_type(::I,::A,::Gs) where {I<:AbstractInterceptionModel,A<:AbstractAModel,Gs<:AbstractGsModel}

    return LeafModels
end

function get_component_type(::A,::Gs) where {A<:AbstractAModel,Gs<:AbstractGsModel}

    return LeafModels
end

"""
    get_component_type(processes)

Return the component type (the actual struct) given the processes passed as a named Tuple.

It is considered a `Component` if it presents models for `interception` and `energy` only.
"""
function get_component_type(::I,::E) where {I<:AbstractInterceptionModel,E<:AbstractEnergyModel}

    return Component
end

function get_component_type(::I) where I<:AbstractInterceptionModel

    return Component
end

# Default get_component_type if no component match the models inputed:
function get_component_type(processes...)
    error("Can't find any component type to hold models: $processes");
end

"""
    get_process(x)

Return the process type (the actual struct) given its name passed as a String.
"""
function get_process(x)
    x = lowercase(x)
    # All possible ways to write the processes in the input (compared to lowecase x):
    processes = Dict("interception" => "interception",
                        "energy" => "energy",
                        "photosynthesis" => "photosynthesis",
                        "stomatalconductance" => "stomatal_conductance",
                        "stomatal_conductance" => "stomatal_conductance")

    if !(haskey(processes,lowercase(x)))
        @warn "Process `$x` is not implemented yet. Did you make a typo?"
        return missing
    end

    return processes[x]
end

"""
    get_model(x)

Return the model (the actual struct) given its name passed as a String.
"""
function get_model(x,process)
    process = lowercase(process)
    if process == "photosynthesis"
        dict = Dict("farquharenbalance" => Fvcb, "fvcb" => Fvcb,
                    "fvcbiter" => FvcbIter, "ignore" => Ignore)
        # NB: dict keys all in lowercase because we transform x into lowercase too to avoid mismatches
    elseif process == "stomatalconductance" || process == "stomatal_conductance"
        dict = Dict("medlyn" => Medlyn)
    elseif process == "interception"
        dict = Dict("translucent" => Translucent, "ignore" => Ignore)
    elseif process == "energy"
        dict = Dict("monteith" => Monteith, "ignore" => Ignore)
    end

    x_lc = lowercase(x)

    !haskey(dict, x_lc) && error("Model type `$x` does not exist for process $process. ",
                                    "Available models are: $(keys(dict))");

    return dict[x_lc]
end

"""
    instantiate(x)

Instantiate a model given its parameter names, considering that parameter names can be
different compared to the model fields (used to insure compatibility with Archimed).
"""
function instantiate(model,param,correspondance,param_type)

    param_names = fieldnames(model)

    # For each parameter, we first search if there is the parameter named as in the fields of the input struct.
    # If yes, we add the key => value pair to param_model. If not, we try with a different name given by "correspondance".
    # If we still don't find it, we try to build the struct with default values and take those values. And if if we can't,
    # we return an error.
    param_model = Dict{Symbol,Any}()
    for i in param_names
        key = string(i)
        if haskey(param,key)
            # The parameter is found directly in param
            push!(param_model, i => convert(param_type[key],param[key]))
        elseif haskey(correspondance, i)
            # The parameter was not found in param, so we try using other names from correspondance
            push!(param_model, i => convert(param_type[key],param[correspondance[i]]))
        end
    end

    no_values = setdiff(collect(param_names),collect(keys(param_model)))
    if length(no_values) > 0
        @info "Using default values for parameters $no_values in model $model"  maxlog = 1
    end

    return model(;param_model...)
end

function instantiate(model::Union{Type{Fvcb},Type{FvcbIter}},param)
    correspondance = Dict(:Tᵣ => "tempCRef", :VcMaxRef => "vcMaxRef", :JMaxRef => "jMaxRef",
                        :RdRef => "rdRef", :θ => "theta")
    # Create a Dict holding the parameter type for each parameter
    param_type = Dict([string(i) => Float64 for i in fieldnames(model)]...)
    instantiate(model,param,correspondance,param_type)
end

function instantiate(model::Type{Monteith},param)
    correspondance = Dict(:ash => "aₛₕ", :asv => "aₛᵥ", :epsilon => "ε",:lambda => "ΔT")
    # Create a Dict holding the parameter type for each parameter
    param_type = Dict("aₛₕ" => Int, "aₛᵥ" => Int, "ε" => Float64,
                        "maxiter" => Int, "ΔT" => Float64)
    instantiate(model,param,correspondance,param_type)
end

function instantiate(model::Type{Medlyn},param)
    correspondance = Dict()
    param_type = Dict([string(i) => Float64 for i in fieldnames(model)]...)
    instantiate(model,param,correspondance,param_type)
end

function instantiate(model::Type{Translucent},param)

    if haskey(param,"optical_properties")
        isPAR = haskey(param["optical_properties"],"PAR")
        isNIR = haskey(param["optical_properties"],"NIR")

        if isPAR && isNIR
            param["optical_properties"] =
                instantiate(σ,param["optical_properties"],[],
                            Dict("PAR" => Float64,"NIR" => Float64))
        else
            missing_optic = ["PAR","NIR"][[!isPAR,!isNIR]]
            error("Missing Optical properties found in `$model` model for `$missing_optic`.")
        end
    else
        error("Optical properties not found in $model model. Please check file.")
    end

    correspondance = Dict()
    param_type = Dict("transparency" => Float64, "optical_properties" => OpticalProperties)

    instantiate(model,param,correspondance,param_type)
end

function instantiate(model::Type{Ignore},param)
    missing
end

"""
    is_model(model)

Check if a model object has the"Group" and "Type" keys as the first level of a Dict type
object. But the function is generic as long as the input struct has a `keys()` method.

# Examples

```julia
models = read_model("path_to_a_model_file.yaml")
is_model(models)
```
"""
function is_model(model)
    collect(keys(model)) == ["Group", "Type"]
end


text:
using SuiteSparseGraphBLAS
using SparseArrays
using Test
using Random
using ChainRulesTestUtils
using ChainRulesCore
using FiniteDifferences
using SuiteSparseGraphBLAS
Random.seed!(1)

function include_test(path)
    println("Testing $path:")
    @time include(path)
end

function ChainRulesTestUtils.rand_tangent(
    rng::AbstractRNG,
    x::GBMatrix{T}
) where {T <: Union{AbstractFloat, Complex}}
    n = nnz(x)
    v = rand(rng, -9:0.01:9, n)
    I, J, _ = findnz(x)
    return GBMatrix(I, J, v; nrows = size(x, 1), ncols = size(x, 2))
end

function ChainRulesTestUtils.rand_tangent(
    rng::AbstractRNG,
    x::GBVector{T}
) where {T <: Union{AbstractFloat, Complex}}
    n = nnz(x)
    v = rand(rng, -9:0.01:9, n)
    I, _ = findnz(x)
    return GBVector(I, v; nrows = size(x, 1))
end

ChainRulesTestUtils.rand_tangent(::AbstractRNG, ::SuiteSparseGraphBLAS.AbstractOp) = NoTangent()

println("Testing SuiteSparseGraphBLAS.jl")
@testset "SuiteSparseGraphBLAS" begin

    include_test("gbarray.jl")
    include_test("operations.jl")
    include_test("chainrules/chainrulesutils.jl")
    include_test("chainrules/mulrules.jl")
    include_test("chainrules/ewiserules.jl")
    include_test("chainrules/selectrules.jl")
    include_test("chainrules/constructorrules.jl")
end

ext: jl


text:
#
# types.jl --
#
# Type definitions for the Julia interface to the C libraries of TAO, a Toolkit
# for Adaptive Optics.
#
#------------------------------------------------------------------------------
#
# This file is part of TAO software (https://git-cral.univ-lyon1.fr/tao)
# licensed under the MIT license.
#
# Copyright (C) 2018-2021, Éric Thiébaut.
#
"""

Type `Tao.AbstractCamera{T}` is the super-type of camera and image provider
concrete types in TAO.  Parameter `T` is the pixel type of the acquired images
or `Any` when the pixel type is unknown or undetermined.

"""
abstract type AbstractCamera{T} end

"""
    TaoBindings.LockMode(val)

is used to denote the lock mode of a lockable object (see
[`TaoBindings.Lockable`](@ref)).  Constants `TaoBindings.UNLOCKED`
`TaoBindings.READ_ONLY`, and `TaoBindings.READ_WRITE` are the different
possibilities.

"""
struct LockMode
    mode::Cint
end

const UNLOCKED   = LockMode(0)
const READ_ONLY  = LockMode(1)
const READ_WRITE = LockMode(2)

"""

`TaoBindings.AbstractSharedObject` is the super-type of all objects stored in
shared memory.

"""
abstract type AbstractSharedObject end

"""

Type `TaoBindings.SharedObject` is used to represent a generic shared TAO
object in Julia.  TAO shared objects implement the `obj.key` syntax with the
following properties:

| Name              | Const. | Description                                                |
|:------------------|:-------|:-----------------------------------------------------------|
| `accesspoint`     | yes    | Address of the server owning the object                    |
| `lock`            | no     | Type of lock owned by the caller                           |
| `owner`           | yes    | Name of the server owning the object                       |
| `shmid`           | yes    | Identifier of the shared memory segment storing the object |
| `size`            | yes    | Number of bytes allocated for the shared object            |
| `type`            | yes    | Type identifier of the shared object                       |

Column *Const.* indicates whether the property is constant during shared object
lifetime.

!!! warn
        Properties should all be considered as read-only by the end-user and never
    directly modified or unexpected behavior may occur.

"""
mutable struct SharedObject <: AbstractSharedObject
    ptr::Ptr{AbstractSharedObject}
    lock::LockMode
    final::Bool    # a finalizer has been installed
    # Provide a unique inner constructor which forces starting with a NULL
    # pointer and no finalizer.
    SharedObject() = new(C_NULL, UNLOCKED, false)
end

"""

Type `TaoBindings.SharedArray{T,N}` is a concrete subtype of `DenseArray{T,N}`
which includes all arrays where elements are stored contiguously in
column-major order.  TAO shared arrrays implement the `arr.key` syntax with the
following properties:

| Name              | Const. | Description                                                |
|:------------------|:-------|:-----------------------------------------------------------|
| `accesspoint`     | yes    | Address of the server owning the object                    |
| `counter`         | no     | Serial number of the shared array                          |
| `lock`            | no     | Type of lock owned by the caller                           |
| `owner`           | yes    | Name of the server owning the object                       |
| `shmid`           | yes    | Identifier of the shared memory segment storing the object |
| `size`            | yes    | Number of bytes allocated for the shared object            |
| `timestamp`       | no     | Time-stamp  of the shared array                            |
| `type`            | yes    | Type identifier of the shared object                       |

Column *Const.* indicates whether the property is constant during shared object lifetime.

!!! warn
    Properties should all be considered as read-only by the end-user and never
    directly modified or unexpected behavior may occur.

"""
mutable struct SharedArray{T,N} <: DenseArray{T,N}
    ptr::Ptr{AbstractSharedObject}
    arr::Array{T,N}
    lock::LockMode
    final::Bool    # a finalizer has been installed
end

"""
    SpinnakerCameras.RemoteCameraState
    enumeration of the RemoteCamera States

""" RemoteCameraState
struct RemoteCameraState
    state::Cint
end
const STATE_UNKNOWN = RemoteCameraState(0)
const STATE_INIT    = RemoteCameraState(1)
const STATE_WAIT    = RemoteCameraState(2)
const STATE_ERROR   = RemoteCameraState(3)
const STATE_WORK    = RemoteCameraState(4)
const STATE_QUIT    = RemoteCameraState(5)

"""
    SpinnakercCameras.RemoteCameraCommand
enumeration of the Remote Camera Commands

""" RemoteCameraCommand
struct RemoteCameraCommand
    cmd::Cint
end

const   CMD_INIT  = RemoteCameraCommand(0)
const   CMD_WAIT  = RemoteCameraCommand(1)
const   CMD_WORK = RemoteCameraCommand(2)
const   CMD_STOP  = RemoteCameraCommand(3)
const   CMD_ABORT  = RemoteCameraCommand(4)
const   CMD_RESET  = RemoteCameraCommand(5)
const   CMD_CONFIG  = RemoteCameraCommand(6)
const   CMD_UPDATE = RemoteCameraCommand(7)
const   CMD_QUIT  = RemoteCameraCommand(8)

struct ShCamSIG
    sig::Cint
end
const   SIG_DONE = ShCamSIG(0)
const   SIG_OK = ShCamSIG(1)
const   SIG_ERROR = ShCamSIG(2)

"""

Type `TaoBindings.SharedCamera` is used to represent shared camera data in
Julia. It served as an interface to camera's configuration within a camera server.
A shared camera instance implements the `cam.key` syntax with the following
public properties:

| Name              | Const. | Description                                                |
|:------------------|:-------|:-----------------------------------------------------------|
| `accesspoint`     | yes    | Address of the server owning the object                    |
| `bufferrencoding` | no     | Encoding of the camera acquisition buffers                 |
| `counter`         | no     | Counter of the last acquired frame                         |
| `exposuretime`    | no     | Exposure time in seconds per frame                         |
| `framerate`       | no     | Frames per second                                          |
| `height`          | no     | Height of the ROI in macro pixels                          |
| `last`            | no     | Shared memory identifier of the last acquired image        |
| `listlength`      | yes    | Number of shared images memorized by the camera owner      |
| `lock`            | no     | Type of lock owned by the caller                           |
| `next`            | no     | Shared memory identifier of the next acquired image        |
| `owner`           | yes    | Name of the server owning the object                       |
| `pixeltype`       | no     | Pixel type of the axquired images                          |
| `sensorencoding`  | no     | Encoding of the pixel data sent by the device              |
| `sensorheight`    | yes    | Number of rows of physical pixels of the sensor            |
| `sensorwidth`     | yes    | Number of physical pixels per row of the sensor            |
| `shmid`           | yes    | Identifier of the shared memory segment storing the object |
| `size`            | yes    | Number of bytes allocated for the shared object            |
| `state`           | no     | State of the remote camera                                 |
| `type`            | yes    | Type identifier of the shared object                       |
| `width`           | no     | Width of the ROI in macro pixels                           |
| `xbin`            | no     | Horizontal binning factor                                  |
| `xoff`            | no     | Horizontal offset of the ROI                               |
| `ybin`            | no     | Vertical binning factor                                    |
| `yoff`            | no     | Vertical offset of the ROI                                 |

Notes: *ROI* is the Region Of Interest of the acquired image.  ROI offsets are
a number of physical pixels.  Binning factors are in physical pixels per macro
pixel.  Column *Const.* indicates whether the property is constant during
shared object lifetime.  To make sure that the values of non-immutable fields
are consistent, the camera should be locked by the caller.  For example:

    timeout = 30.0 # 30 seconds timeout
    if rdlock(cam, timeout)
        # Camera `cam` has been succesfuly locked for read-only access.
        stat = cam.state
        counter = cam.counter
        last = cam.last
        unlock(cam) # do not forget to release the lock as soon as possible
    else
        # Time-out occured before read-only access can be granted.
        ...
    end

!!! warn
    Properties should all be considered as read-only by the end-user and never
    directly modified or unexpected behavior may occur.

"""
mutable struct SharedCamera <: AbstractCamera{Any}

    ptr::Ptr{AbstractSharedObject}
    img_config::ImageConfigContext  # file
    attachedCam::Int8
    cameras::Vector{Camera}

    listlength::Int8        # number of images memorized my the camera owner
    last::Int16
    next::Int16
    lastTS::Int16
    nextTS::Int16

    lock::LockMode
    final::Bool    # a finalizer has been installed

    # Provide a unique inner constructor which forces starting with a NULL
    # pointer and no finalizer.
    SharedCamera() = new(C_NULL,ImageConfigContext(),0,Vector{Camera}(undef,5),5,1,0,0,0,UNLOCKED, false)
end

# The following is to have a complete signature for type statbility.
const DynamicArray{T,N} = ResizableArray{T,N,Vector{T}}

"""
    RemoteCamera{T}(dev) -> cam

wraps TAO shared camera `dev` into a higher level camera instance.  The camera
instance `dev` can also be replaced by the remote camera name.  For instance:

    cam = RemoteCamera{T}("TAO:SpinnakerCameras")

The parameter `T` is the pixel type of the acquired images.  If not specified,
it is obtained from the associated shared camera.  In any cases, it will be
asserted that `T` matches the element type of the shared arrays storing the
acquired images (and their weights).

A remote camera instance implements the `cam.key` syntax with the same public
properties as a shared camera plus

| Name              | Const. | Description                                                |
|:------------------|:-------|:-----------------------------------------------------------|
|`shmids`           | no     | list of shmids of shared array                             |
|`img`              | no     | attached shared array of image data                        |
|`imgTime`          | no     | attached shared array of image timestamp                   |
|`cmds`             | no     | attached shared array of command                           |
|
"""

mutable struct RemoteCamera{T<:Number} <: AbstractCamera{T}
    #data buffers imitating grabber
    arrays::Vector{Array{T,2}} # list of attached shared arrays
    timestamps::Vector{Float64}         # list of timestamps of the shared array

    shmids::Vector{ShmId}            # list of corresponding identifiers
    device::SharedCamera             # connection to remote camera
    time_origin::HighResolutionTime     # timestamp when the server is up

    cmds::SharedArray{Cint,1}           # command storage
    img::SharedArray{T,2}                # last image
    imgTime::SharedArray{Float64,1}       # last image timestamp
    no_cmds::Base.Condition                # cinditional variable for commanding

    worker_pid::Integer                 # worker pid for acquisition interrupting

    function RemoteCamera{T}(device::SharedCamera,dims::NTuple{2,Int64}) where {T<:Number}
        isconcretetype(T) || error("pixel type $T must be concrete")
        len = Int(device.listlength)
        arrays = fill!(Vector{Array{T,2}}(undef, len),zeros(dims))
        shmids = fill!(Vector{ShmId}(undef, len), -1)
        timestamps = fill!(Vector{UInt64}(undef,len), 0)

        _cmds= create(SharedArray{Cint,1},2)
        cmds = attach(SharedArray, _cmds.shmid)

        imgBuff = create(SharedArray{T,2},dims)
        img = attach(SharedArray, imgBuff.shmid)

        imgTimeBuff = create(SharedArray{UInt64,1},1)
        imgTime = attach(SharedArray, imgTimeBuff.shmid)

        wrlock(cmds, 0.5) do
            fill!(cmds,-1)
        end

        wrlock(img, 0.5) do
            fill!(img,convert(T,0))
        end

        wrlock(imgTime, 0.5) do
            fill!(imgTime,0)
        end

        return new{T}(arrays, timestamps, shmids, device,HighResolutionTime(0,0),cmds,img,imgTime,Condition(),0)
    end
end

_to_Cint(state::RemoteCameraState) =  state.state
_to_Cint(cmd::RemoteCameraCommand) =  cmd.cmd
_to_Cint(sig::ShCamSIG) = sig.sig

"""

`TaoBindings.Lockable` is the union of types of TAO objects that implement
read/write locks.  Methods [`TaoBindings.rdlock`](@ref),
[`TaoBindings.wrlock`](@ref), [`TaoBindings.unlock`](@ref), and
[`TaoBindings.islocked`](@ref) are applicable to such objects.

"""
const Lockable = Union{AbstractSharedObject,SharedArray,AbstractMonitor, SharedCamera}

"""

Union `TaoBindings.AnySharedObject` is defined to represent any shared objects
in `TaoBindings` because shared arrays and shared cameras inherit from
`DenseArray` and `AbstractCamera` respectively, not from
`TaoBindings.AbstractSharedObject`.

"""
const AnySharedObject = Union{AbstractSharedObject,SharedArray,SharedCamera,RemoteCamera,AbstractMonitor}

const TaoSharedObject = Union{AbstractSharedObject,SharedArray}

"""
The singleton type `Basic` is a *trait* used to indicate that the version
provided by Julia must be used for a vectorized method.

This *hack* is to avoid calling methods that may be inefficient in a specific
context.  For instance BLAS `lmul!(A,B)` for small arrays.

"""
struct Basic end


text:
module GetDigit_n
    using Printf

    const MAX_PRECISION = 300
    const π_str = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798"

    function getn(func, maxn, precision, half_precision)
        nlo = 1
        nhi = maxn

        # 正しいnを二分探索で求める
        while nhi - nlo > 1
            n = (nhi + nlo) >> 1

            setprecision(MAX_PRECISION)
            res = abs(func(n) - BigFloat(π))
            if half_precision && res < BigFloat(5) * BigFloat(10)^(BigFloat(-precision - 1))
                nhi = n
            elseif !half_precision && res < BigFloat(10)^(BigFloat(-precision - 1))
                nhi = n
            else
                nlo = n
            end

            @printf("n = %d, nlo = %d, nhi = %d, res = %.100f\n", n, nlo, nhi, res)
        end

        if nhi != maxn
            return nhi
        else
            throw(DomainError(maxn, "MAXNの値が少なすぎる"))
        end
    end

    function getn_even(func, maxn, precision, half_precision)
        nlo = 2
        nhi = maxn

        # 正しいnを二分探索で求める
        while nhi - nlo > 2
            n = (nhi + nlo) >> 1
            n = n & 1 == 0 ? n : n + 1

            setprecision(MAX_PRECISION)
            res = abs(func(n) - BigFloat(pi))
            if half_precision && res < BigFloat(5) * BigFloat(10)^(BigFloat(-precision - 1))
                nhi = n
            elseif !half_precision && res < BigFloat(10)^(BigFloat(-precision - 1))
                nhi = n
            else
                nlo = n
            end

            @printf("n = %d, nlo = %d, nhi = %d, res = %.100f\n", n, nlo, nhi, res)
        end

        if nhi != maxn
            return nhi
        else
            throw(DomainError(maxn, "MAXNの値が少なすぎる"))
        end
    end

    function getn_linear(func, maxn, precision, half_precision)
        for n = 1:maxn + 1
            if n == maxn + 1
                throw(DomainError(maxn, "MAXNの値が少なすぎる"))
            end

            setprecision(MAX_PRECISION)
            res = abs(func(n) - BigFloat(π))
            @printf("n = %d, res = %.50f\n", n, res)

            if half_precision && res < BigFloat(5) * BigFloat(10)^(BigFloat(-precision - 1))
                return n
            elseif !half_precision && res < BigFloat(10)^(BigFloat(-precision - 1))
                return n
            end
        end
    end

    function getdigit(func, n, precision, half_precision)
        plo = precision
        phi = MAX_PRECISION

        # 正しい精度を二分探索で求める
        while phi - plo > 1
            p = (phi + plo) >> 1

            setprecision(p)
            res = abs(func(n) - BigFloat(π))
            if half_precision && res < BigFloat(5) * BigFloat(10)^(BigFloat(-precision - 1))
                phi = p
            elseif !half_precision && res < BigFloat(10)^(BigFloat(-precision - 1))
                phi = p
            else
                plo = p
            end

            @printf("p = %d, plo = %d, phi = %d, res = %.100f\n", p, plo, phi, res)
        end

        if phi != MAX_PRECISION
            return phi
        else
            throw(DomainError(MAX_PRECISION, "MAXNの値が少なすぎる"))
        end
    end

    function getdigit_2(func, n, digit, precision)
        for p = precision:-1:digit
            setprecision(p)

            s = roundoff(func(n), digit)
            @printf("p = %d, res = %s\n", p, s)
            if s != roundoff(π_str, digit)
                setprecision(p + 1)

                return roundoff(func(n), digit) == roundoff(π_str, digit) ? p + 1 : getdigit_3(func, n, digit, p + 1)
            end
        end

        throw(DomainError("謎のエラー"))
    end

    function getdigit_3(func, n, digit, precision)
        p = precision

        while true
            setprecision(p)

            s = roundoff(func(n), digit)
            @printf("p = %d, res = %s\n", p, s)
            if s == roundoff(π_str, digit)
                break
            end

            p += 1
        end

        return p
    end

    function roundoff(value::BigFloat, digit)
        fmt = "%." * string(digit) * "f"

        return @eval @sprintf($fmt, $value)
    end

    function roundoff(value::String, digit)
        res = collect(value)[1:digit + 3]
        if res[digit + 3] >= '5' && res[digit + 2] == '9'
            res[digit + 2] = '0'
            res[digit + 1] = Char(res[digit + 1] + 1)
        elseif res[digit + 3] >= '5'
            res[digit + 2] = Char(res[digit + 2] + 1)
        end

        return join(res[1:digit + 2])
    end
end


text:
module NCS
using ..Common, PiecewiseIncreasingRanges
import Compat.String
export readncs

immutable NCSContinuousChannel <: ContinuousChannel
    header::String
    data::Vector{Int16}
    times::PiecewiseIncreasingRange{Float64,StepRange{Int64,Int64},Int64}
    voltage_multiplier::Float64
end

immutable NCSRecordHeader
    qwTimeStamp::UInt64
    dwChannelNumber::UInt32
    dwSampleFreq::UInt32
    dwNumValidSamples::UInt32
end

Base.read(io::IO, ::Type{NCSRecordHeader}) =
    NCSRecordHeader(read(io, UInt64), read(io, UInt32), read(io, UInt32), read(io, UInt32))

function compute_times(rec::NCSRecordHeader, sample_multiplier::Int64, step::Int64)
    first_sample = Int64(rec.qwTimeStamp)*sample_multiplier
    first_sample:step:first_sample+step*(rec.dwNumValidSamples-1)
end

function readncs(filename::AbstractString; kwargs...)
    io = open(filename, "r")
    try
        readncs(io, filesize(io); kwargs...)
    finally
        close(io)
    end
end

function readncs(io::IO, sz::Union{Integer,Void}=nothing; skip_nonmonotonic::Bool=false)
    header = rstrip(String(read(io, UInt8, 16384)), '\0')
    eof(io) && return NCSContinuousChannel(header, Int16[], PiecewiseIncreasingRange(StepRange{Int,Int}[], 1))

    nrecs::Int = isa(sz, Integer) ? div(sz-16384, 1044) : -1
    sample_buffer = Array(Int16, 512)
    sample_buffer_reinterp = reinterpret(UInt8, sample_buffer)
    samples = Array(Int16, isa(sz, Integer) ? nrecs*512 : 512)
    nsamples = 0
    times = Array(StepRange{Int,Int}, isa(sz, Integer) ? nrecs : 1)

    # Read first header so we can save its channel number to make
    # sure we only have one
    rec1 = read(io, NCSRecordHeader)
    read!(io, sample_buffer)
    copy!(samples, nsamples+1, sample_buffer, 1, rec1.dwNumValidSamples)
    nsamples += rec1.dwNumValidSamples

    # Figure out range multipliers

    # This would be correct if the reported sample rate were:
    # @compat divisor = lcm(Int64(rec1.dwSampleFreq), 10^6)
    # sample_multiplier = div(divisor, 10^6)
    # @compat step = div(divisor, Int64(rec1.dwSampleFreq))

    # However, it seems that the reported sample rate is wrong. The
    # actual sample rate is rounded so that the delta between
    # timestamps is constant.
    sample_multiplier = 512
    divisor = 512*10^6
    step = div(divisor, rec1.dwSampleFreq)

    nrecs = 1
    reccount = 1
    times[1] = compute_times(rec1, sample_multiplier, step)

    while !eof(io)
        reccount += 1
        rec = read(io, NCSRecordHeader)
        rec.dwChannelNumber == rec1.dwChannelNumber || error("only one channel supported per file")
        rec.dwSampleFreq == rec1.dwSampleFreq || error("sample rate is non-constant")
        read!(io, sample_buffer_reinterp)
        rectimes = compute_times(rec, sample_multiplier, step)
        if first(rectimes) < last(times[nrecs])
            if skip_nonmonotonic
                warn("""timestamp at record $(reccount) is not monotonically increasing
                        (previous end time = $(last(times[nrecs])/divisor), start time = $(first(rectimes)/divisor); skipping""")
                continue
            else
                error("""timestamp at record $(reccount) is not monotonically increasing.
                         Use readncs(..., skip_nonmotonoic=true) to skip nonmonotonic timestamps""")
            end
        end
        isa(sz, Void) && resize!(samples, nsamples+rec.dwNumValidSamples)
        copy!(samples, nsamples+1, sample_buffer, 1, rec.dwNumValidSamples)
        nsamples += rec.dwNumValidSamples
        nrecs += 1
        if isa(sz, Integer)
            times[nrecs] = rectimes
        else
            push!(times, rectimes)
        end
    end

    resize!(samples, nsamples)
    resize!(times, nrecs)

    voltage_multiplier = NaN
    m = match(r"\n\s*-ADBitVolts\s+([0-9.]+)", header)
    if m !== nothing
        voltage_multiplier = parse(Float64, m.captures[1])
    end

    return NCSContinuousChannel(header, samples, PiecewiseIncreasingRange(times, divisor), voltage_multiplier)
end

end


text:
module Tables

using LinearAlgebra, DataValueInterfaces, DataAPI, TableTraits, IteratorInterfaceExtensions

export rowtable, columntable

if !hasmethod(getproperty, Tuple{Tuple, Int})
    Base.getproperty(t::Tuple, i::Int) = t[i]
end

"""
    Tables.AbstractColumns

An interface type defined as an ordered set of columns that support
retrieval of individual columns by name or index. A retrieved column
must be an indexable collection with known length, i.e. an object
that supports `length(col)` and `col[i]` for any `i = 1:length(col)`.
`Tables.columns` must return an object that satisfies the `Tables.AbstractColumns` interface.
While `Tables.AbstractColumns` is an abstract type that custom "columns" types may subtype for
useful default behavior (indexing, iteration, property-access, etc.), users should not use it
for dispatch, as Tables.jl interface objects **are not required** to subtype, but only
implement the required interface methods.

Interface definition:

| Required Methods                                         | Default Definition          | Brief Description                                                                                |
|----------------------------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Tables.getcolumn(table, i::Int)`                        | getfield(table, i)          | Retrieve a column by index                                                                                |
| `Tables.getcolumn(table, nm::Symbol)`                    | getproperty(table, nm)      | Retrieve a column by name                                                                                |
| `Tables.columnnames(table)`                              | propertynames(table)        | Return column names for a table as an indexable collection                                                                                |
| **Optional methods**                                     |                             |                                                                                |
| `Tables.getcolumn(table, ::Type{T}, i::Int, nm::Symbol)` | Tables.getcolumn(table, nm) | Given a column eltype `T`, index `i`, and column name `nm`, retrieve the column. Provides a type-stable or even constant-prop-able mechanism for efficiency. |

While types aren't required to subtype `Tables.AbstractColumns`, benefits of doing so include:
  * Indexing interface defined (using `getcolumn`); i.e. `tbl[i]` will retrieve the column at index `i`
  * Property access interface defined (using `columnnames` and `getcolumn`); i.e. `tbl.col1` will retrieve column named `col1`
  * Iteration interface defined; i.e. `for col in table` will iterate each column in the table
  * `AbstractDict` methods defined (`get`, `haskey`, etc.) for checking and retrieving columns
  * A default `show` method
This allows a custom table type to behave as close as possible to a builtin `NamedTuple` of vectors object.
"""
abstract type AbstractColumns end

"""
    Tables.AbstractRow

Abstract interface type representing the expected `eltype` of the iterator returned from `Tables.rows(table)`.
`Tables.rows` must return an iterator of elements that satisfy the `Tables.AbstractRow` interface.
While `Tables.AbstractRow` is an abstract type that custom "row" types may subtype for
useful default behavior (indexing, iteration, property-access, etc.), users should not use it
for dispatch, as Tables.jl interface objects **are not required** to subtype, but only
implement the required interface methods.

Interface definition:

| Required Methods                                       | Default Definition        | Brief Description                                                                                |
|--------------------------------------------------------|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Tables.getcolumn(row, i::Int)`                        | getfield(row, i)          | Retrieve a column value by index                                                                                |
| `Tables.getcolumn(row, nm::Symbol)`                    | getproperty(row, nm)      | Retrieve a column value by name                                                                                |
| `Tables.columnnames(row)`                              | propertynames(row)        | Return column names for a row as an indexable collection                                                                                |
| **Optional methods**                                   |                           |                                                                                |
| `Tables.getcolumn(row, ::Type{T}, i::Int, nm::Symbol)` | Tables.getcolumn(row, nm) | Given a column element type `T`, index `i`, and column name `nm`, retrieve the column value. Provides a type-stable or even constant-prop-able mechanism for efficiency. |

While custom row types aren't required to subtype `Tables.AbstractRow`, benefits of doing so include:
  * Indexing interface defined (using `getcolumn`); i.e. `row[i]` will return the column value at index `i`
  * Property access interface defined (using `columnnames` and `getcolumn`); i.e. `row.col1` will retrieve the value for the column named `col1`
  * Iteration interface defined; i.e. `for x in row` will iterate each column value in the row
  * `AbstractDict` methods defined (`get`, `haskey`, etc.) for checking and retrieving column values
  * A default `show` method
This allows the custom row type to behave as close as possible to a builtin `NamedTuple` object.
"""
abstract type AbstractRow end

"""
    Tables.getcolumn(::AbstractColumns, nm::Symbol) => Indexable collection with known length
    Tables.getcolumn(::AbstractColumns, i::Int) => Indexable collection with known length
    Tables.getcolumn(::AbstractColumns, T, i::Int, nm::Symbol) => Indexable collection with known length

    Tables.getcolumn(::AbstractRow, nm::Symbol) => Column value
    Tables.getcolumn(::AbstractRow, i::Int) => Column value
    Tables.getcolumn(::AbstractRow, T, i::Int, nm::Symbol) => Column value

Retrieve an entire column (from `AbstractColumns`) or single row column value (from an `AbstractRow`) by column name (`nm`), index (`i`),
or if desired, by column element type (`T`), index (`i`), and name (`nm`). When called on a `AbstractColumns` interface object,
the returned object should be an indexable collection with known length. When called on a `AbstractRow` interface
object, it returns the single column value. The methods taking a single `Symbol` or `Int` are both required
for the `AbstractColumns` and `AbstractRow` interfaces; the third method is optional if type stability is possible.
The default definition of `Tables.getcolumn(x, i::Int)` is `getfield(x, i)`. The default definition of
`Tables.getcolumn(x, nm::Symbol)` is `getproperty(x, nm)`.
"""
function getcolumn end

getcolumn(x, i::Int) = getfield(x, i)
getcolumn(x, nm::Symbol) = getproperty(x, nm)
getcolumn(x, ::Type{T}, i::Int, nm::Symbol) where {T} = getcolumn(x, nm)
getcolumn(x::NamedTuple{names, types}, ::Type{T}, i::Int, nm::Symbol) where {names, types, T} = Core.getfield(x, i)

"""
    Tables.columnnames(::Union{AbstractColumns, AbstractRow}) => Indexable collection

Retrieves the list of column names as an indexable collection (like a `Tuple` or `Vector`) for a `AbstractColumns` or `AbstractRow` interface object. The default definition calls `propertynames(x)`.
"""
function columnnames end

columnnames(x) = propertynames(x)

"""
    Tables.isrowtable(x) => Bool

For convenience, some table objects that are naturally "row oriented" can
define `Tables.isrowtable(::Type{TableType}) = true` to simplify satisfying the
Tables.jl interface. Requirements for defining `isrowtable` include:
  * `Tables.rows(x) === x`, i.e. the table object itself is a `Row` iterator
  * If the table object is mutable, it should support:
    * `push!(x, row)`: allow pushing a single row onto table
    * `append!(x, rows)`: allow appending set of rows onto table
  * If table object is mutable and indexable, it should support:
    * `x[i] = row`: allow replacing of a row with another row by index

A table object that defines `Tables.isrowtable` will have definitions for
`Tables.istable`, `Tables.rowaccess`, and `Tables.rows` automatically defined.
"""
function isrowtable end

isrowtable(::T) where {T} = isrowtable(T)
isrowtable(::Type{T}) where {T} = false
# to avoid ambiguities
isrowtable(::Type{T}) where {T <: AbstractVector{Union{}}} = false

# default definitions for AbstractDict to act as an AbstractColumns or AbstractRow
getcolumn(x::AbstractDict{Symbol}, i::Int) = x[columnnames(x)[i]]
getcolumn(x::AbstractDict{Symbol}, nm::Symbol) = x[nm]
getcolumn(x::AbstractDict{Symbol}, ::Type{T}, i::Int, nm::Symbol) where {T} = x[nm]
columnnames(x::AbstractDict{Symbol}) = collect(keys(x))

# AbstractVector of Dicts for Tables.rows
const DictRows = AbstractVector{T} where {T <: AbstractDict{Symbol}}
isrowtable(::Type{<:DictRows}) = true
# DictRows doesn't naturally lend itself to the `Tables.schema` requirement
# we can't just look at the first row, because the types might change,
# row-to-row (e.g. `missing`, then `1.1`, etc.). Therefore, the safest option
# is to just return `nothing`
schema(x::DictRows) = nothing

# Dict of AbstractVectors for Tables.columns
const DictColumns = AbstractDict{K, V} where {K <: Symbol, V <: AbstractVector}
istable(::Type{<:DictColumns}) = true
columnaccess(::Type{<:DictColumns}) = true
columns(x::DictColumns) = x
schema(x::DictColumns) = Schema(collect(keys(x)), eltype.(values(x)))

# for other AbstractDict, let's throw an informative error
columns(x::T) where {T <: AbstractDict} = error("to treat $T as a table, it must have a key type of `Symbol`, and a value type `<: AbstractVector`")

# default definitions for AbstractRow, AbstractColumns
const RorC = Union{AbstractRow, AbstractColumns}

Base.IteratorSize(::Type{R}) where {R <: RorC} = Base.HasLength()
Base.length(r::RorC) = length(columnnames(r))
Base.IndexStyle(::Type{<:RorC}) = Base.IndexLinear()
Base.firstindex(r::RorC) = 1
Base.lastindex(r::RorC) = length(r)
Base.getindex(r::RorC, i::Int) = getcolumn(r, i)
Base.getindex(r::RorC, nm::Symbol) = getcolumn(r, nm)
Base.getproperty(r::RorC, nm::Symbol) = getcolumn(r, nm)
Base.getproperty(r::RorC, i::Int) = getcolumn(r, i)
Base.propertynames(r::RorC) = columnnames(r)
Base.keys(r::RorC) = columnnames(r)
Base.values(r::RorC) = collect(r)
Base.haskey(r::RorC, key::Symbol) = key in columnnames(r)
Base.haskey(r::RorC, i::Int) = 0 < i < length(columnnames(r))
Base.get(r::RorC, key::Union{Integer, Symbol}, default) = haskey(r, key) ? getcolumn(r, key) : default
Base.get(f::Base.Callable, r::RorC, key::Union{Integer, Symbol}) = haskey(r, key) ? getcolumn(r, key) : f()
Base.iterate(r::RorC, i=1) = i > length(r) ? nothing : (getcolumn(r, i), i + 1)
Base.isempty(r::RorC) = length(r) == 0

function Base.NamedTuple(r::RorC)
    names = columnnames(r)
    return NamedTuple{Tuple(Base.map(Symbol, names))}(Tuple(getcolumn(r, nm) for nm in names))
end

function Base.show(io::IO, x::T) where {T <: RorC}
    if get(io, :compact, false) || get(io, :limit, false)
        print(io, "$T: ")
        show(io, NamedTuple(x))
    else
        println(io, "$T:")
        names = collect(columnnames(x))
        values = [getcolumn(x, nm) for nm in names]
        Base.print_matrix(io, hcat(names, values))
    end
end

# AbstractRow AbstractVector as Rows
const AbstractRowTable = AbstractVector{T} where {T <: AbstractRow}
isrowtable(::Type{<:AbstractRowTable}) = true
schema(x::AbstractRowTable) = nothing

# AbstractColumns as Columns
istable(::Type{<:AbstractColumns}) = true
columnaccess(::Type{<:AbstractColumns}) = true
columns(x::AbstractColumns) = x
schema(x::AbstractColumns) = nothing

"""
    Tables.Row(row)

Convenience type to wrap any `AbstractRow` interface object in a dedicated struct
to provide useful default behaviors (allows any `AbstractRow` to be used like a `NamedTuple`):
  * Indexing interface defined; i.e. `row[i]` will return the column value at index `i`, `row[nm]` will return column value for column name `nm`
  * Property access interface defined; i.e. `row.col1` will retrieve the value for the column named `col1`
  * Iteration interface defined; i.e. `for x in row` will iterate each column value in the row
  * `AbstractDict` methods defined (`get`, `haskey`, etc.) for checking and retrieving column values
"""
struct Row{T} <: AbstractRow
    x::T
end

Row(x::AbstractRow) = x

"""
    Tables.Columns(columns)

Convenience type to wrap any `AbstractColumns` interface object in a dedicated struct
to provide useful default behaviors (allows any `AbstractColumns` to be used like a `NamedTuple` of `Vectors`):
  * Indexing interface defined; i.e. `row[i]` will return the column at index `i`, `row[nm]` will return column for column name `nm`
  * Property access interface defined; i.e. `row.col1` will retrieve the value for the column named `col1`
  * Iteration interface defined; i.e. `for x in row` will iterate each column in the row
  * `AbstractDict` methods defined (`get`, `haskey`, etc.) for checking and retrieving columns
"""
struct Columns{T} <: AbstractColumns
    x::T
end

Columns(x::AbstractColumns) = x

const RorC2 = Union{Row, Columns}

getx(x::RorC2) = getfield(x, :x)

getcolumn(x::RorC2, i::Int) = getcolumn(getx(x), i)
getcolumn(x::RorC2, nm::Symbol) = getcolumn(getx(x), nm)
getcolumn(x::RorC2, ::Type{T}, i::Int, nm::Symbol) where {T} = getcolumn(getx(x), T, i, nm)
columnnames(x::RorC2) = columnnames(getx(x))

"""
    Tables.istable(x) => Bool

Check if an object has specifically defined that it is a table. Note that
not all valid tables will return true, since it's possible to satisfy the
Tables.jl interface at "run-time", e.g. a `Generator` of `NamedTuple`s iterates
`NamedTuple`s, which satisfies the `AbstractRow` interface, but there's no static way
of knowing that the generator is a table.
"""
function istable end

istable(x::T) where {T} = istable(T) || TableTraits.isiterabletable(x) === true
istable(::Type{T}) where {T} = isrowtable(T)
# to avoid ambiguities
istable(::Type{T}) where {T <: AbstractVector{Union{}}} = false
istable(::AbstractVector{Union{}}) = false

"""
    Tables.rowaccess(x) => Bool

Check whether an object has specifically defined that it implements the `Tables.rows`
function that does _not_ copy table data.  That is to say, `Tables.rows(x)` must be done
with O(1) time and space complexity when `Tables.rowaccess(x) == true`.  Note that
`Tables.rows` will work on any object that iterates `AbstractRow`-compatible objects, even if
they don't define `rowaccess`, e.g. a `Generator` of `NamedTuple`s.  However, this
generic fallback may copy the data from input table `x`.  Also note that just because
an object defines `rowaccess` doesn't mean a user should call `Tables.rows` on it;
`Tables.columns` will also work, providing a valid `AbstractColumns` object from the rows.
Hence, users should call `Tables.rows` or `Tables.columns` depending on what is most
natural for them to *consume* instead of worrying about what and how the input is oriented.
"""
function rowaccess end

rowaccess(x::T) where {T} = rowaccess(T)
rowaccess(::Type{T}) where {T} = isrowtable(T)

"""
    Tables.columnaccess(x) => Bool

Check whether an object has specifically defined that it implements the `Tables.columns`
function that does _not_ copy table data.  That is to say, `Tables.columns(x)` must be done
with O(1) time and space complexity when `Tables.columnaccess(x) == true`.  Note that
`Tables.columns` has generic fallbacks allowing it to produces `AbstractColumns` objects, even if
the input doesn't define `columnaccess`.  However, this generic fallback may copy the data
from input table `x`.  Also note that just because an object defines `columnaccess` doesn't
mean a user should call `Tables.columns` on it; `Tables.rows` will also work, providing a
valid `AbstractRow` iterator. Hence, users should call `Tables.rows` or `Tables.columns` depending
on what is most natural for them to *consume* instead of worrying about what and how the
input is oriented.
"""
function columnaccess end

columnaccess(x::T) where {T} = columnaccess(T)
columnaccess(::Type{T}) where {T} = false

"""
    Tables.schema(x) => Union{Nothing, Tables.Schema}

Attempt to retrieve the schema of the object returned by `Tables.rows` or `Tables.columns`.
If the `AbstractRow` iterator or `AbstractColumns` object can't determine its schema, `nothing` will be returned.
Otherwise, a `Tables.Schema` object is returned, with the column names and types available for use.
"""
function schema end

schema(x) = nothing

"""
    Tables.materializer(x) => Callable

For a table input, return the "sink" function or "materializing" function that can take a
Tables.jl-compatible table input and make an instance of the table type. This enables "transform"
workflows that take table inputs, apply transformations, potentially converting the table to
a different form, and end with producing a table of the same type as the original input. The
default materializer is `Tables.columntable`, which converts any table input into a `NamedTuple`
of `Vector`s.
"""
function materializer end

materializer(x::T) where {T} = materializer(T)
materializer(::Type{T}) where {T} = columntable

"""
    Tables.columns(x) => AbstractColumns-compatible object

Accesses data of input table source `x` by returning an [`AbstractColumns`](@ref)-compatible
object, which allows retrieving entire columns by name or index. A retrieved column
is an object that is indexable and has a known length, i.e. supports
`length(col)` and `col[i]` for any `i = 1:length(col)`. Note that
even if the input table source is row-oriented by nature, an efficient generic
definition of `Tables.columns` is defined in Tables.jl to build a `AbstractColumns`-
compatible object object from the input rows.

The [`Tables.Schema`](@ref) of a `AbstractColumns` object can be queried via `Tables.schema(columns)`,
which may return `nothing` if the schema is unknown.
Column names can always be queried by calling `Tables.columnnames(columns)`, and individual columns
can be accessed by calling `Tables.getcolumn(columns, i::Int )` or `Tables.getcolumn(columns, nm::Symbol)`
with a column index or name, respectively.
"""
function columns end

"""
    Tables.rows(x) => Row iterator

Accesses data of input table source `x` row-by-row by returning an [`AbstractRow`](@ref)-compatible iterator.
Note that even if the input table source is column-oriented by nature, an efficient generic
definition of `Tables.rows` is defined in Tables.jl to return an iterator of row views into
the columns of the input.

The [`Tables.Schema`](@ref) of an `AbstractRow` iterator can be queried via `Tables.schema(rows)`,
which may return `nothing` if the schema is unknown.
Column names can always be queried by calling `Tables.columnnames(row)` on an individual row,
and row values can be accessed by calling `Tables.getcolumn(rows, i::Int )` or
`Tables.getcolumn(rows, nm::Symbol)` with a column index or name, respectively.

See also [`rowtable`](@ref) and [`namedtupleiterator`](@ref).
"""
function rows end

# Schema implementation
"""
    Tables.Schema(names, types)

Create a `Tables.Schema` object that holds the column names and types for an `AbstractRow` iterator
returned from `Tables.rows` or an `AbstractColumns` object returned from `Tables.columns`.
`Tables.Schema` is dual-purposed: provide an easy interface for users to query these properties,
as well as provide a convenient "structural" type for code generation.

To get a table's schema, one can call `Tables.schema` on the result of `Tables.rows` or `Tables.columns`,
but also note that a table may return `nothing`, indicating that its column names and/or column element types
are unknown (usually not inferrable). This is similar to the `Base.EltypeUnknown()` trait for iterators
when `Base.IteratorEltype` is called. Users should account for the `Tables.schema(tbl) => nothing` case
by using the properties of the results of `Tables.rows(x)` and `Tables.columns(x)` directly.

To access the names, one can simply call `sch.names` to return the tuple of Symbols.
To access column element types, one can similarly call `sch.types`, which will return a tuple of types (like `(Int64, Float64, String)`).

The actual type definition is
```julia
struct Schema{names, types} end
```
Where `names` is a tuple of Symbols, and `types` is a tuple _type_ of types (like `Tuple{Int64, Float64, String}`).
Encoding the names & types as type parameters allows convenient use of the type in generated functions
and other optimization use-cases.
"""
struct Schema{names, types} end
Schema(names::Tuple{Vararg{Symbol}}, types::Type{T}) where {T <: Tuple} = Schema{names, T}()
Schema(::Type{NamedTuple{names, types}}) where {names, types} = Schema{names, types}()

# pass through Ints to allow Tuples to act as rows
sym(x) = Symbol(x)
sym(x::Int) = x

Schema(names, ::Nothing) = Schema{Tuple(Base.map(sym, names)), nothing}()
Schema(names, types) = Schema{Tuple(Base.map(sym, names)), Tuple{types...}}()

function Base.show(io::IO, sch::Schema{names, types}) where {names, types}
    println(io, "Tables.Schema:")
    Base.print_matrix(io, hcat(collect(names), types === nothing ? fill(nothing, length(names)) : collect(fieldtype(types, i) for i = 1:fieldcount(types))))
end

function Base.getproperty(sch::Schema{names, types}, field::Symbol) where {names, types}
    if field === :names
        return names
    elseif field === :types
        return types === nothing ? nothing : Tuple(fieldtype(types, i) for i = 1:fieldcount(types))
    else
        throw(ArgumentError("unsupported property for Tables.Schema"))
    end
end

Base.propertynames(sch::Schema) = (:names, :types)

# partitions

"""
    Tables.partitions(x)

Request a "table" iterator from `x`. Each iterated element must be a "table" in the sense
that one may call `Tables.rows` or `Tables.columns` to get a row-iterator or collection
of columns. All iterated elements _must_ have identical schema, so that users may call
`Tables.schema(first_element)` on the first iterated element and know that each
subsequent iteration will match the same schema. The default definition is:
```julia
Tables.partitions(x) = (x,)
```
So that any input is assumed to be a single "table". This means users should feel free
to call `Tables.partitions` anywhere they're currently calling `Tables.columns` or
`Tables.rows`, and get back an iterator of those instead. In other words, "sink" functions
can use `Tables.partitions` whether or not the user passes a partionable table, since the
default is to treat a single input as a single, non-partitioned table.

`Tables.partitioner(itr)`(@ref) is a convenience wrapper to provide table partitions
from any table iterator; this allows for easy wrapping of a `Vector` or iterator of tables
as valid partitions, since by default, they'd be treated as a single table.

A 2nd convenience method is provided with the defintion:
```julia
Tables.partitions(x...) = x
```
That allows passing vararg tables and they'll be treated as separate partitions. Sink
functions may allow vararg table inputs and can "splat them through" to `partitions`.

For convenience, `Tables.partitions(Iterators.partition(...))` is defined for cases
where user-controlled partitioning is desired over an applicable input (an input iterator).
"""
partitions(x) = (x,)
partitions(x...) = x
partitions(x::Iterators.PartitionIterator) = x

"""
    Tables.LazyTable(f, arg)

A "table" type that delays materialization until `Tables.columns` or `Tables.rows` is called.
This allows, for example, sending a `LazyTable` to a remote process or thread which can
then call `Tables.columns` or `Tables.rows` to "materialize" the table. Is used by default
in `Tables.partitioner(f, itr)` where a materializer function `f` is passed to each element
of an iterable `itr`, allowing distributed/concurrent patterns like:

```julia
for tbl in Tables.partitions(Tables.partitioner(CSV.File, list_of_csv_files))
    Threads.@spawn begin
        cols = Tables.columns(tbl)
        # do stuff with cols
    end
end
```
In this example, `CSV.File` will be called like `CSV.File(x)` for each element of the
`list_of_csv_files` iterable, but _not until_ `Tables.columns(tbl)` is called, which
in this case happens in a thread-spawned task, allowing files to be parsed and processed
in parallel.
"""
struct LazyTable{F, T}
    f::F
    x::T
end

columns(x::LazyTable) = columns(x.f(x.x))
rows(x::LazyTable) = rows(x.f(x.x))

struct Partitioner{T}
    x::T
end

"""
    Tables.partitioner(f, itr)
    Tables.partitioner(x)

Convenience methods to generate table iterators. The first method takes a "materializer"
function `f` and an iterator `itr`, and will call `Tables.LazyTable(f, x) for x in itr`
for each iteration. This allows delaying table materialization until `Tables.columns`
or `Tables.rows` are called on the `LazyTable` object (which will call `f(x)`). This
allows a common desired pattern of materializing and processing a table on a remote
process or thread, like:

```julia
for tbl in Tables.partitions(Tables.partitioner(CSV.File, list_of_csv_files))
    Threads.@spawn begin
        cols = Tables.columns(tbl)
        # do stuff with cols
    end
end
```

The second method is provided because the default behavior of `Tables.partition(x)`
is to treat `x` as a single, non-partitioned table. This method allows users to easily
wrap a `Vector` or generator of tables as table partitions to pass to sink functions
able to utilize `Tables.partitions`.
"""
partitioner(x) = Partitioner(x)
partitioner(f, itr) = partitioner((LazyTable(f, x) for x in itr))

partitions(x::Partitioner) = x

Base.IteratorEltype(::Type{Partitioner{S}}) where {S} = Base.IteratorEltype(S)
Base.eltype(x::Partitioner{S}) where {S} = eltype(x.x)
Base.eltype(::Type{Partitioner{S}}) where {S} = eltype(S)
Base.IteratorSize(::Type{Partitioner{S}}) where {S} = Base.IteratorSize(S)
Base.length(x::Partitioner) = length(x.x)
Base.size(x::Partitioner) = size(x.x)
Base.iterate(x::Partitioner, st...) = iterate(x.x, st...)

const SPECIALIZATION_THRESHOLD = 100

# helper functions
include("utils.jl")

# reference implementations: Vector of NamedTuples and NamedTuple of Vectors
include("namedtuples.jl")

# generic fallback definitions
include("fallbacks.jl")

# allow any valid iterator to be a table
include("tofromdatavalues.jl")

# matrix integration
include("matrix.jl")

# dict tables
include("dicts.jl")

"""
    Tables.columnindex(table, name::Symbol)

Return the column index (1-based) of a column by `name` in a table with a known schema; returns 0 if `name` doesn't exist in table
"""
columnindex(table, colname::Symbol) = columnindex(schema(table), colname)

"""
    Tables.columntype(table, name::Symbol)

Return the column element type of a column by `name` in a table with a known schema; returns Union{} if `name` doesn't exist in table
"""
columntype(table, colname::Symbol) = columntype(schema(table), colname)

Base.@pure columnindex(::Schema{names, types}, name::Symbol) where {names, types} = columnindex(names, name)

"given names and a Symbol `name`, compute the index (1-based) of the name in names"
Base.@pure function columnindex(names::Tuple{Vararg{Symbol}}, name::Symbol)
    i = 1
    for nm in names
        nm === name && return i
        i += 1
    end
    return 0
end

Base.@pure columntype(::Schema{names, types}, name::Symbol) where {names, types} = columntype(names, types, name)

"given tuple type and a Symbol `name`, compute the type of the name in the tuples types"
Base.@pure function columntype(names::Tuple{Vararg{Symbol}}, ::Type{types}, name::Symbol) where {types <: Tuple}
    i = 1
    for nm in names
        nm === name && return fieldtype(types, i)
        i += 1
    end
    return Union{}
end

end # module


text:
# ---
# jupyter:
#   jupytext:
#     formats: ipynb,jl:hydrogen
#     text_representation:
#       extension: .jl
#       format_name: hydrogen
#       format_version: '1.3'
#       jupytext_version: 1.10.3
#   kernelspec:
#     display_name: Julia 1.6.3
#     language: julia
#     name: julia-1.6
# ---

# %%
using Distributions

struct TwoBinomials{T<:Real} <: DiscreteMultivariateDistribution
    m::Int
    p::T
    n::Int
    q::T
end

Base.length(d::TwoBinomials) = 4
Base.eltype(d::TwoBinomials) = Int
Distributions.params(d::TwoBinomials) = (d.m, d.p, d.n, d.q)

function Distributions._rand!(rng::Distributions.AbstractRNG, d::TwoBinomials, x::AbstractVector)
    k = rand(rng, Binomial(d.m, d.p))
    l = rand(rng, Binomial(d.n, d.q))
    @inbounds x[1], x[2], x[3], x[4] = k, l, d.m - k, d.n - l
    x
end

function Distributions._rand!(rng::Distributions.AbstractRNG, d::TwoBinomials, x::AbstractMatrix)
    @inbounds for j in axes(x, 2)
        k = rand(rng, Binomial(d.m, d.p))
        l = rand(rng, Binomial(d.n, d.q))
        x[1,j], x[2,j], x[3,j], x[4,j] = k, l, d.m - k, d.n - l
    end
    x
end

function Distributions._logpdf(d::TwoBinomials, x::AbstractVector)
    x[1] + x[3] == d.m || x[2] + x[4] == d.m || return -Inf
    logpdf(Binomial(d.m, d.p), x[1]) + logpdf(Binomial(d.n, d.q), x[2])
end

Distributions.mean(d::TwoBinomials) = [d.m*d.p, d.n*d.q, d.m*(1 - d.p), d.n*(1 - d.q)]

# %%
d = TwoBinomials(10, 0.25, 20, 0.75)

# %%
params(d)

# %%
rand(d)

# %%
rand(d, 16)

# %%
pdf(d, [3, 16, d.m-3, d.n-16])

# %%
mean(d)

# %%

max_stars_repo_name: genkuroki/public

max_issues_repo_name: genkuroki/public

max_forks_repo_name: genkuroki/public


text:
function points(games::AbstractArray{<:AbstractString})::Int
    function score(it::AbstractString)::Int
        it[1]  > it[3] && return 3
        it[1] == it[3] && return 1
        0
    end
    sum(score, games)
end

max_stars_repo_name: UlrichBerntien/Codewars-Katas

max_issues_repo_name: UlrichBerntien/Codewars-Katas

max_forks_repo_name: UlrichBerntien/Codewars-Katas


text:
module ImageShow

using FileIO
using ImageCore, OffsetArrays
import ImageBase: restrict

using StackViews
using ImageCore.MappedArrays
using ImageCore.PaddedViews

include("showmime.jl")
include("gif.jl")
include("multipage.jl")
include("compat.jl")

# facilitate testing from importers
testdir() = joinpath(@__DIR__,"..","test")

end