text:
# LibFTD2XX.jl - C Library Wrapper
#
# By Reuben Hill 2019, Gowerlabs Ltd, reuben@gowerlabs.co.uk
#
# Copyright (c) Gowerlabs Ltd.
#
# This module contains wrappers for D2XX devices. See D2XX Programmer's Guide
# (FT_000071) for more information. Function names match those in the library.
#
# Only recommended for advanced users. Note that there is minimal argument
# checking in these wrapper methods.

module Wrapper

# Type Aliases
export DWORD, ULONG, UCHAR

# Library Constants
export FT_OPEN_BY_SERIAL_NUMBER, FT_OPEN_BY_DESCRIPTION, FT_OPEN_BY_LOCATION
export FT_DEVICE
export FT_LIST_NUMBER_ONLY, FT_LIST_BY_INDEX, FT_LIST_ALL
export FT_BITS_8, FT_BITS_7
export FT_STOP_BITS_1, FT_STOP_BITS_2
export FT_PARITY_NONE, FT_PARITY_ODD, FT_PARITY_EVEN, FT_PARITY_MARK, FT_PARITY_SPACE
export FT_FLOW_NONE, FT_FLOW_RTS_CTS, FT_FLOW_DTR_DSR, FT_FLOW_XON_XOFF
export FT_EVENT_RXCHAR, FT_EVENT_MODEM_STATUS, FT_EVENT_LINE_STATUS
export FT_PURGE_RX, FT_PURGE_TX
export FT_MODE_RESET,
       FT_MODE_ASYNC_BITBANG,
       FT_MODE_MPSSE,
       FT_MODE_SYNC_BITBANG,
       FT_MODE_MCU_EMULATION,
       FT_MODE_FAST_OPTO,
       FT_MODE_CBUS_BITBANG,
       FT_MODE_SCS_FIFO
export FT_STATUS_ENUM,
       FT_OK,
       FT_INVALID_HANDLE,
       FT_DEVICE_NOT_FOUND,
       FT_DEVICE_NOT_OPENED,
       FT_IO_ERROR,
       FT_INSUFFICIENT_RESOURCES,
       FT_INVALID_PARAMETER,
       FT_INVALID_BAUD_RATE,
       FT_DEVICE_NOT_OPENED_FOR_ERASE,
       FT_DEVICE_NOT_OPENED_FOR_WRITE,
       FT_FAILED_TO_WRITE_DEVICE,
       FT_EEPROM_READ_FAILED,
       FT_EEPROM_WRITE_FAILED,
       FT_EEPROM_ERASE_FAILED,
       FT_EEPROM_NOT_PRESENT,
       FT_EEPROM_NOT_PROGRAMMED,
       FT_INVALID_ARGS,
       FT_NOT_SUPPORTED,
       FT_OTHER_ERROR,
       FT_DEVICE_LIST_NOT_READY

# Types
export FT_HANDLE

# Functions
export FT_CreateDeviceInfoList,
       FT_GetDeviceInfoList,
       FT_GetDeviceInfoDetail,
       FT_ListDevices,
       FT_Open,
       FT_OpenEx,
       FT_Close,
       FT_Read,
       FT_Write,
       FT_SetBaudRate,
       FT_SetDataCharacteristics,
       FT_SetTimeouts,
       FT_SetFlowControl,
       FT_SetDtr,
       FT_ClrDtr,
       FT_SetRts,
       FT_ClrRts,
       FT_GetModemStatus,
       FT_GetQueueStatus,
       FT_GetDeviceInfo,
       FT_GetDriverVersion,
       FT_GetLibraryVersion,
       FT_GetStatus,
       FT_SetEventNotification,
       FT_SetChars,
       FT_SetBreakOn,
       FT_SetBreakOff,
       FT_Purge,
       FT_ResetDevice,
       FT_StopInTask,
       FT_RestartInTask,
       FT_GetLatencyTimer,
       FT_SetLatencyTimer,
       FT_SetBitMode,
       FT_GetBitMode,
       FT_SetUSBParameters

using Libdl
using libftd2xx_jll

# Type Aliases
#
const DWORD     = UInt32
const ULONG     = UInt64
const USHORT    = UInt16
const UCHAR     = UInt8
const FT_STATUS = DWORD

# Library Constants
#

# FT_OpenEx Flags
const FT_OPEN_BY_SERIAL_NUMBER  = 1
const FT_OPEN_BY_DESCRIPTION    = 2
const FT_OPEN_BY_LOCATION       = 4

# FT_GetDeviceInfo FT_DEVICE Type Enum
@enum(
  FT_DEVICE,
  FT_DEVICE_232BM    = DWORD(0),
  FT_DEVICE_232AM    = DWORD(1),
  FT_DEVICE_100AX    = DWORD(2),
  FT_DEVICE_UNKNOWN  = DWORD(3),
  FT_DEVICE_2232C    = DWORD(4),
  FT_DEVICE_232R     = DWORD(5),
  FT_DEVICE_2232H    = DWORD(6),
  FT_DEVICE_4232H    = DWORD(7),
  FT_DEVICE_232H     = DWORD(8),
  FT_DEVICE_X_SERIES = DWORD(9)
)

# FT_ListDevices Flags (used in conjunction with FT_OpenEx Flags)
const FT_LIST_NUMBER_ONLY     = 0x80000000
const FT_LIST_BY_INDEX        = 0x40000000
const FT_LIST_ALL             = 0x20000000

# Word Lengths
const FT_BITS_8               = 8
const FT_BITS_7               = 7

# Stop Bits
const FT_STOP_BITS_1          = 0
const FT_STOP_BITS_2          = 2

# Parity
const  FT_PARITY_NONE         = 0
const  FT_PARITY_ODD          = 1
const  FT_PARITY_EVEN         = 2
const  FT_PARITY_MARK         = 3
const  FT_PARITY_SPACE        = 4

# FT_SetFlowControl Flow Control Flags
const FT_FLOW_NONE            = 0x0000
const FT_FLOW_RTS_CTS         = 0x0100
const FT_FLOW_DTR_DSR         = 0x0200
const FT_FLOW_XON_XOFF        = 0x0400

# FT_SetEventNotification Event Flags (not yet implemented)
const FT_EVENT_RXCHAR         = 1
const FT_EVENT_MODEM_STATUS   = 2
const FT_EVENT_LINE_STATUS    = 4

# FT_Purge Flags
const FT_PURGE_RX = 1
const FT_PURGE_TX = 2

# Mode Flags
const FT_MODE_RESET          = 0x00
const FT_MODE_ASYNC_BITBANG  = 0x01
const FT_MODE_MPSSE          = 0x02
const FT_MODE_SYNC_BITBANG   = 0x04
const FT_MODE_MCU_EMULATION  = 0x08
const FT_MODE_FAST_OPTO      = 0x10
const FT_MODE_CBUS_BITBANG   = 0x20
const FT_MODE_SCS_FIFO       = 0x40

# FT_STATUS Return Values
@enum(
  FT_STATUS_ENUM,
  FT_OK,
  FT_INVALID_HANDLE,
  FT_DEVICE_NOT_FOUND,
  FT_DEVICE_NOT_OPENED,
  FT_IO_ERROR,
  FT_INSUFFICIENT_RESOURCES,
  FT_INVALID_PARAMETER,
  FT_INVALID_BAUD_RATE,
  FT_DEVICE_NOT_OPENED_FOR_ERASE,
  FT_DEVICE_NOT_OPENED_FOR_WRITE,
  FT_FAILED_TO_WRITE_DEVICE,
  FT_EEPROM_READ_FAILED,
  FT_EEPROM_WRITE_FAILED,
  FT_EEPROM_ERASE_FAILED,
  FT_EEPROM_NOT_PRESENT,
  FT_EEPROM_NOT_PROGRAMMED,
  FT_INVALID_ARGS,
  FT_NOT_SUPPORTED,
  FT_OTHER_ERROR,
  FT_DEVICE_LIST_NOT_READY)

# Types
#

"""
    struct FT_DEVICE_LIST_INFO_NODE

Julia language representation of the `FT_DEVICE_LIST_INFO_NODE` structure which
is passed to `FT_GetDeviceInfoList`.

Pre-allocated arrays of `Cchar` (in julia, represented as `NTuple{L, Cchar}`)
are filled by `FT_GetDeviceInfoList` with null terminated strings. They can be
converted to julia strings using [`ntuple2string`](@ref).
"""
struct FT_DEVICE_LIST_INFO_NODE
  flags::DWORD
  typ::DWORD
  id::DWORD
  locid::DWORD
  serialnumber::NTuple{16, Cchar}
  description::NTuple{64, Cchar}
  fthandle_ptr::Ptr{Cvoid}
end

"""
    mutable struct FT_HANDLE <: IO

Holds a handle to an FT D2XX device.
"""
mutable struct FT_HANDLE <: IO
  p::Ptr{Cvoid}
end

"""
    FT_HANDLE()

Constructs a handle to an FT D2XX device that points to `C_NULL`. Initialsed
with a finalizer that calls [`destroy!`](@ref).
"""
function FT_HANDLE()
  handle = FT_HANDLE(C_NULL)
  finalizer(destroy!, handle)
  handle
end

"""
    destroy!(handle::FT_HANDLE)

Destructor for the [`FT_HANDLE`](@ref) type.
"""
function destroy!(handle::FT_HANDLE)
  if _ptr(handle) != C_NULL
    FT_Close(handle)
  end
end

## Type Accessors
#

"""
    _ptr(handle::FT_HANDLE)

Get the raw pointer for an [`FT_HANDLE`](@ref).
"""
_ptr(handle::FT_HANDLE) = handle.p

"""
    _ptr(handle::FT_HANDLE, fthandle_ptr::Ptr{Cvoid})

Set the raw pointer for an [`FT_HANDLE`](@ref).
"""
_ptr(handle::FT_HANDLE, fthandle_ptr::Ptr{Cvoid}) = (handle.p = fthandle_ptr)

# Utility functions
#

# Internal use only
function check(status::FT_STATUS)
  FT_STATUS_ENUM(status) == FT_OK ||  throw(FT_STATUS_ENUM(status))
end

# wrapper functions
#

"""
    FT_CreateDeviceInfoList()

# Example
```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> "Number of devices is \$numdevs"
"Number of devices is 4"
```
"""
function FT_CreateDeviceInfoList()
  lpdwNumDevs = Ref{DWORD}(0)
  status = ccall((:FT_CreateDeviceInfoList, libftd2xx), cdecl, FT_STATUS,
                 (Ref{DWORD},),
                 lpdwNumDevs)
  check(status)
  lpdwNumDevs[]
end

"""
    FT_GetDeviceInfoList(lpdwNumDevs)

# Arguments
 - `lpdwNumDevs`: The number of devices.

# Example
```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> devinfolist, numdevs = FT_GetDeviceInfoList(numdevs);

julia> numdevs
0x00000004

julia> using LibFTD2XX.Util # for ntuple2string

julia> ntuple2string(devinfolist[1].description)
"USB <-> Serial Converter D"

julia> devinfolist[1].fthandle_ptr
Ptr{Nothing} @0x0000000000000000

julia> devinfolist[1].locid
0x00000000

julia> devinfolist[1].typ
0x00000007

julia> devinfolist[1].flags
0x00000002

julia> devinfolist[1].id
0x04036011

julia> ntuple2string(devinfolist[1].serialnumber)
"FT3AD2HCD"

```
"""
function FT_GetDeviceInfoList(lpdwNumDevs)
  pDest =  Vector{FT_DEVICE_LIST_INFO_NODE}(undef, lpdwNumDevs)
  status = ccall((:FT_GetDeviceInfoList, libftd2xx), cdecl, FT_STATUS,
                 (Ref{FT_DEVICE_LIST_INFO_NODE}, Ref{DWORD}),
                  pDest,                         Ref{DWORD}(lpdwNumDevs))
  check(status)
  pDest, lpdwNumDevs
end

"""
    FT_GetDeviceInfoDetail(dwIndex)

# Arguments
 - `dwIndex`: Index of entry in the device info list.

# Example
```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> idx, flags, typ, id, locid, serialnumber, description, fthandle = FT_GetDeviceInfoDetail(0) # zero indexed
(0, 0x00000002, 0x00000007, 0x04036011, 0x00000000, "FT3AD2HCD", "USB <-> Serial Converter D", FT_HANDLE(Ptr{Nothing} @0x0000000000000000))
```
"""
function FT_GetDeviceInfoDetail(dwIndex)
  lpdwFlags, lpdwType  = Ref{DWORD}(), Ref{DWORD}()
  lpdwID,    lpdwLocId = Ref{DWORD}(), Ref{DWORD}()
  pcSerialNumber = pointer(Vector{Cchar}(undef, 16))
  pcDescription  = pointer(Vector{Cchar}(undef, 64))
  ftHandle = FT_HANDLE()

  status = ccall((:FT_GetDeviceInfoDetail, libftd2xx), cdecl, FT_STATUS,
  (DWORD,   Ref{DWORD}, Ref{DWORD}, Ref{DWORD}, Ref{DWORD}, Cstring,        Cstring,       Ref{FT_HANDLE}),
   dwIndex, lpdwFlags,  lpdwType,   lpdwID,     lpdwLocId,  pcSerialNumber, pcDescription, ftHandle)

  check(status)
  dwIndex[], lpdwFlags[], lpdwType[], lpdwID[], lpdwLocId[], unsafe_string(pcSerialNumber), unsafe_string(pcDescription), ftHandle
end

"""
    FT_ListDevices(pvArg1, pvArg2, dwFlags)

**NOT FULLY FUNCTIONAL: NOT RECOMMENDED FOR USE**.

# Arguments
 - `pvArg1`: Depends on dwFlags.
 - `pvArg2`: Depends on dwFlags.
- `dwFlags`: Flag which determines format of returned information.

E.g. call with `pvArg1 = Ref{UInt32}()` and/or `pvArg2 = Ref{UInt32}()` for
cases where `pvArg1` and/or `pvArg2` return or are given DWORD information.

# Examples

1. Get number of devices...
```julia-repl

julia> numdevs = Ref{UInt32}();

julia> FT_ListDevices(numdevs, Ref{UInt32}(), FT_LIST_NUMBER_ONLY)

julia> numdevs[]
0x00000004
```

2. Get serial number of first device... *NOT CURRENTLY WORKING*
```julia-repl

julia> devidx = Ref{UInt32}(0)
Base.RefValue{UInt32}(0x00000000)

julia> buffer = pointer(Vector{Cchar}(undef, 64))
Ptr{Int8} @0x0000000004f2e530

julia> FT_ListDevices(devidx, buffer, FT_LIST_BY_INDEX|FT_OPEN_BY_SERIAL_NUMBER)
ERROR: FT_ListDevices wrapper does not yet flags other than FT_LIST_NUMBER_ONLY.
Stacktrace:
...

```
"""
function FT_ListDevices(pvArg1, pvArg2, dwFlags)
  dwFlags == FT_LIST_NUMBER_ONLY || throw(ErrorException("FT_ListDevices wrapper does not yet flags other than FT_LIST_NUMBER_ONLY."))
  flagsarg = DWORD(dwFlags)
  status = ccall((:FT_ListDevices, libftd2xx), cdecl, FT_STATUS,
                 (Ptr{Cvoid}, Ptr{Cvoid}, DWORD),
                  pvArg1,     pvArg2,     dwFlags)
  check(status)
  return
end

"""
    FT_Open(iDevice)

# Arguments
 - `iDevice`: Zero-base index of device to open

# Example

```julia-repl

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000000c4970)

```
"""
function FT_Open(iDevice)
  ftHandle = FT_HANDLE()
  status = ccall((:FT_Open, libftd2xx), cdecl, FT_STATUS, (Int,     Ref{FT_HANDLE}),
                                                     iDevice, ftHandle)
  if FT_STATUS_ENUM(status) != FT_OK
    _ptr(ftHandle, C_NULL)
    throw(FT_STATUS_ENUM(status))
  end
  ftHandle
end

"""
    FT_OpenEx(pvArg1::AbstractString, dwFlags::Integer)

# Arguments
 - `pvArg1::AbstractString` : Either description or serial number depending on
   `dwFlags`.
 - `dwFlags::Integer` : FT_OPEN_BY_DESCRIPTION or FT_OPEN_BY_SERIAL_NUMBER.
   Note that FT_OPEN_BY_LOCATION is not currently supported.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> idx, flags, typ, id, locid, serialnumber, description, fthandle = FT_GetDeviceInfoDetail(0)
(0, 0x00000002, 0x00000007, 0x04036011, 0x00000000, "FT3AD2HCD", "USB <-> Serial Converter D", FT_HANDLE(Ptr{Nothing} @0x0000000000000000))

julia> handle = FT_OpenEx(description, FT_OPEN_BY_DESCRIPTION)
FT_HANDLE(Ptr{Nothing} @0x0000000000dfe740)

julia> isopen(handle)
true

julia> close(handle)

julia> handle = FT_OpenEx(serialnumber, FT_OPEN_BY_SERIAL_NUMBER)
FT_HANDLE(Ptr{Nothing} @0x0000000005448ea0)

julia> isopen(handle)
true

julia> close(handle)
```
"""
function FT_OpenEx(pvArg1::AbstractString, dwFlags::Integer)
  @assert (dwFlags == FT_OPEN_BY_DESCRIPTION) | (dwFlags == FT_OPEN_BY_SERIAL_NUMBER)
  flagsarg = DWORD(dwFlags)
  handle = FT_HANDLE()
  status = ccall((:FT_OpenEx, libftd2xx), cdecl, FT_STATUS,
                 (Cstring , DWORD,    Ref{FT_HANDLE}),
                  pvArg1,   flagsarg, handle)
  if FT_STATUS_ENUM(status) != FT_OK
    _ptr(handle, C_NULL)
    throw(FT_STATUS_ENUM(status))
  end
  handle
end

"""
    FT_Close(ftHandle::FT_HANDLE)

Closes an open device and sets the pointer to C_NULL.

# Example

```julia-repl
julia> julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x000000000010a870)

julia> FT_Close(handle)
```
"""
function FT_Close(ftHandle::FT_HANDLE)
  status = ccall((:FT_Close, libftd2xx), cdecl, FT_STATUS, (FT_HANDLE, ),
                                                      ftHandle)
  check(status)
  _ptr(ftHandle, C_NULL)
  return
end

"""
    FT_Read(ftHandle::FT_HANDLE, lpBuffer::AbstractVector{UInt8}, dwBytesToRead::Integer)

Returns number of bytes read.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> buffer = zeros(UInt8, 2)
2-element Array{UInt8,1}:
 0x00
 0x00

julia> nread = FT_Read(handle, buffer, 0) # read 0 bytes. Returns number read...
0x00000000

julia> buffer # should be unmodified...
2-element Array{UInt8,1}:
 0x00
 0x00

julia> FT_Close(handle)
```
"""
function FT_Read(ftHandle::FT_HANDLE, lpBuffer::AbstractVector{UInt8}, dwBytesToRead::Integer)
  @assert 0 <= dwBytesToRead <= length(lpBuffer)
  lpdwBytesReturned = Ref{DWORD}()
  status = ccall((:FT_Read, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, Ref{UInt8}, DWORD,         Ref{DWORD}),
                 ftHandle,   lpBuffer,   dwBytesToRead, lpdwBytesReturned)
  check(status)
  lpdwBytesReturned[]
end

"""
    FT_Write(ftHandle::FT_HANDLE, lpBuffer::Vector{UInt8}, dwBytesToWrite::Integer)

Returns number of bytes written.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> buffer = ones(UInt8, 2)
2-element Array{UInt8,1}:
 0x01
 0x01

julia> nwr = FT_Write(handle, buffer, 0) # Write 0 bytes...
0x00000000

julia> buffer # should be unmodified...
2-element Array{UInt8,1}:
 0x01
 0x01

julia> nwr = FT_Write(handle, buffer, 2) # Write 2 bytes...
0x00000002

julia> buffer # should be unmodified...
2-element Array{UInt8,1}:
 0x01
 0x01

julia> FT_Close(handle)
```
"""
function FT_Write(ftHandle::FT_HANDLE, lpBuffer::AbstractVector{UInt8}, dwBytesToWrite::Integer)
  @assert 0 <= dwBytesToWrite <= length(lpBuffer)
  lpdwBytesWritten = Ref{DWORD}()
  status = ccall((:FT_Write, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, Ref{UInt8}, DWORD,          Ref{DWORD}),
                  ftHandle,  lpBuffer,   dwBytesToWrite, lpdwBytesWritten)
  check(status)
  lpdwBytesWritten[]
end

"""
    FT_SetBaudRate(ftHandle::FT_HANDLE, dwBaudRate::Integer)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetBaudRate(handle, 115200) # Set baud rate to 115200

julia> FT_Close(handle)
```
"""
function FT_SetBaudRate(ftHandle::FT_HANDLE, dwBaudRate::Integer)
  @assert 0 < dwBaudRate <= typemax(DWORD)
  status = ccall((:FT_SetBaudRate, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, DWORD),
                  ftHandle,    dwBaudRate)
  check(status)
  return
end

"""
    FT_SetDataCharacteristics(ftHandle::FT_HANDLE, uWordLength, uStopBits, uParity)

# Arguments
 - `ftHandle` : device handle
 - `uWordLength` : Bits per word - either FT_BITS_8 or FT_BITS_7
 - `uStopBits` : Stop bits - either FT_STOP_BITS_1 or FT_STOP_BITS_2
 - `uParity` : Parity - either FT_PARITY_EVEN, FT_PARITY_ODD, FT_PARITY_MARK,
   FT_PARITY_SPACE, or FT_PARITY_NONE.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetDataCharacteristics(handle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE)

julia> FT_Close(handle)
```
"""
function FT_SetDataCharacteristics(ftHandle::FT_HANDLE, uWordLength, uStopBits, uParity)
  @assert (uWordLength == FT_BITS_8) || (uWordLength == FT_BITS_7)
  @assert (uStopBits == FT_STOP_BITS_1) || (uStopBits == FT_STOP_BITS_2)
  @assert (uParity == FT_PARITY_EVEN) || (uParity == FT_PARITY_ODD) ||
          (uParity == FT_PARITY_MARK) || (uParity == FT_PARITY_SPACE) ||
          (uParity == FT_PARITY_NONE)
  status = ccall((:FT_SetDataCharacteristics, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, UCHAR,       UCHAR,     UCHAR),
                  ftHandle,  uWordLength, uStopBits, uParity)
  check(status)
  return
end

"""
    FT_SetTimeouts(ftHandle::FT_HANDLE, dwReadTimeout, dwWriteTimeout)

# Arguments
 - `ftHandle` : device handle
 - `dwReadTimeout` : Read timeout (milliseconds)
 - `dwWriteTimeout` : Write timeout (milliseconds)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetBaudRate(handle, 9600)

julia> FT_SetTimeouts(handle, 50, 10) # 50ms read timeout, 10 ms write timeout

julia> buffer = zeros(UInt8, 5000);

julia> @time nwr = FT_Write(handle, buffer, 5000) # writes nothing if timesout
  0.014323 seconds (4 allocations: 160 bytes)
0x00000000

julia> @time nread = FT_Read(handle, buffer, 5000)
  0.049545 seconds (4 allocations: 160 bytes)
0x00000000

julia> FT_Close(handle)
```
"""
function FT_SetTimeouts(ftHandle::FT_HANDLE, dwReadTimeout, dwWriteTimeout)
  status = ccall((:FT_SetTimeouts, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, DWORD,         DWORD,),
                  ftHandle,  dwReadTimeout, dwWriteTimeout)
  check(status)
  return
end

"""
    FT_SetFlowControl(ftHandle::FT_HANDLE, usFlowControl, uXon, uXoff)

# Arguments
 - `ftHandle` : device handle
 - `usFlowControl` :
 - `uXon` :
 - `uXoff` :

# Example

"""
function FT_SetFlowControl(ftHandle::FT_HANDLE, usFlowControl, uXon, uXoff)
  @assert usFlowControl in (FT_FLOW_NONE,FT_FLOW_RTS_CTS,FT_FLOW_DTR_DSR,FT_FLOW_XON_XOFF)
  status = ccall((:FT_SetFlowControl, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, USHORT,        UCHAR, UCHAR,),
                  ftHandle,  usFlowControl, uXon,  uXoff)
  check(status)
  return
end

"""
    FT_SetDtr(ftHandle::FT_HANDLE)

# Example

"""
function FT_SetDtr(ftHandle::FT_HANDLE)
  status = ccall((:FT_SetDtr, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE,),
                  ftHandle,)
  check(status)
  return
end

"""
    FT_ClrDtr(ftHandle::FT_HANDLE)

# Example

"""
function FT_ClrDtr(ftHandle::FT_HANDLE)
  status = ccall((:FT_ClrDtr, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE,),
                  ftHandle,)
  check(status)
  return
end

"""
    FT_SetRts(ftHandle::FT_HANDLE)

# Example

"""
function FT_SetRts(ftHandle::FT_HANDLE)
  status = ccall((:FT_SetRts, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE,),
                  ftHandle,)
  check(status)
  return
end

"""
    FT_ClrRts(ftHandle::FT_HANDLE)

# Example

"""
function FT_ClrRts(ftHandle::FT_HANDLE)
  status = ccall((:FT_ClrRts, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE,),
                  ftHandle,)
  check(status)
  return
end

"""
    FT_GetModemStatus(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> flags = FT_GetModemStatus(handle)
0x00006400

julia> FT_Close(handle)
```
"""
function FT_GetModemStatus(ftHandle::FT_HANDLE)
  lpdwModemStatus = Ref{DWORD}()
  status = ccall((:FT_GetModemStatus, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, Ref{DWORD}),
                  ftHandle,  lpdwModemStatus)
  check(status)
  lpdwModemStatus[]
end

"""
    FT_GetQueueStatus(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> nbrx = FT_GetQueueStatus(handle) # get number of items in recieve queue
0x00000000

julia> FT_Close(handle)
```
"""
function FT_GetQueueStatus(ftHandle::FT_HANDLE)
  lpdwAmountInRxQueue = Ref{DWORD}()
  status = ccall((:FT_GetQueueStatus, libftd2xx), cdecl, FT_STATUS,
                  (FT_HANDLE, Ref{DWORD}),
                   ftHandle,  lpdwAmountInRxQueue)
  check(status)
  lpdwAmountInRxQueue[]
end

"""
    FT_GetDeviceInfo(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> typ, id, serialnumber, description = FT_GetDeviceInfo(handle);

julia> FT_Close(handle)
```
"""
function FT_GetDeviceInfo(ftHandle::FT_HANDLE)
  pftType = Ref{FT_DEVICE}()
  lpdwID = Ref{DWORD}()
  pcSerialNumber = pointer(Vector{Cchar}(undef, 16))
  pcDescription  = pointer(Vector{Cchar}(undef, 64))
  pvDummy = C_NULL

  status = ccall((:FT_GetDeviceInfo, libftd2xx), cdecl, FT_STATUS,
  (FT_HANDLE, Ref{FT_DEVICE}, Ref{DWORD}, Cstring,        Cstring,       Ptr{Cvoid}),
   ftHandle,  pftType,        lpdwID,     pcSerialNumber, pcDescription, pvDummy)

  check(status)
  pftType[], lpdwID[], unsafe_string(pcSerialNumber), unsafe_string(pcDescription)
end

if Sys.iswindows()

  """
      FT_GetDriverVersion(ftHandle::FT_HANDLE)

  # Example

  ```julia-repl
  julia> numdevs = FT_CreateDeviceInfoList()
  0x00000004

  julia> handle = FT_Open(0)
  FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

  julia> version = FT_GetDriverVersion(handle)
  0x00021212

  julia> patch = version & 0xFF
  0x00000012

  julia> minor = (version >> 8) & 0xFF
  0x00000012

  julia> major = (version >> 16) & 0xFF
  0x00000002

  julia> VersionNumber(major,minor,patch)
  v"2.18.18"

  julia> FT_Close(handle)
  ```
  """
  function FT_GetDriverVersion(ftHandle::FT_HANDLE)
    lpdwDriverVersion = Ref{DWORD}()
    status = ccall((:FT_GetDriverVersion, libftd2xx), cdecl, FT_STATUS,
                  (FT_HANDLE, Ref{DWORD}),
                    ftHandle,  lpdwDriverVersion)
    check(status)
    lpdwDriverVersion[]
  end

  """
      FT_GetLibraryVersion()

  # Example

  ```julia-repl

  julia> version = FT_GetLibraryVersion()
  0x00021212

  julia> patch = version & 0xFF
  0x00000012

  julia> minor = (version >> 8) & 0xFF
  0x00000012

  julia> major = (version >> 16) & 0xFF
  0x00000002

  julia> VersionNumber(major,minor,patch)
  v"2.18.18"
  ```
  """
  function FT_GetLibraryVersion()
    lpdwDLLVersion = Ref{DWORD}()
    status = ccall((:FT_GetLibraryVersion, libftd2xx), cdecl, FT_STATUS,
                  (Ref{DWORD},),
                    lpdwDLLVersion)
    check(status)
    version = lpdwDLLVersion[]
  end

end # Sys.iswindows()

"""
    FT_GetStatus(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> nbrx, nbtx, eventstatus = FT_GetStatus(handle)
(0x00000000, 0x00000000, 0x00000000)

julia> FT_Close(handle)
```
"""
function FT_GetStatus(ftHandle::FT_HANDLE)
  lpdwAmountInRxQueue, lpdwAmountInTxQueue  = Ref{DWORD}(), Ref{DWORD}()
  lpdwEventStatus = Ref{DWORD}()
  status = ccall((:FT_GetStatus, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, Ref{DWORD},          Ref{DWORD},          Ref{DWORD}),
                  ftHandle,  lpdwAmountInRxQueue, lpdwAmountInTxQueue, lpdwEventStatus)
  check(status)
  lpdwAmountInRxQueue[], lpdwAmountInTxQueue[], lpdwEventStatus[]
end

"""
    FT_SetEventNotification(ftHandle::FT_HANDLE, dwEventMask, pvArg)

Sets conditions for event notification.

An application can use this function to setup conditions which allow a thread to block
until one of the conditions is met. Typically, an application will create an event,
call this function, then block on the event. When the conditions are met, the event is set,
and the application thread unblocked. `dwEventMask` is a bit-map that describes the events
the application is interested in. `pvArg` is interpreted as the handle of an event which
has been created by the application. If one of the event conditions is met, the event is
set. If `dwEventMask=FT_EVENT_RXCHAR`, the event will be set when a character has been
received by the device. If `dwEventMask=FT_EVENT_MODEM_STATUS`, the event will be set when
a change in the modem signals has been detected by the device.
If `dwEventMask=FT_EVENT_LINE_STATUS`, the event will be set when a change in the line
status has been detected by the device.
"""
function FT_SetEventNotification(ftHandle::FT_HANDLE, dwEventMask, pvArg)
  @assert dwEventMask in (FT_EVENT_RXCHAR,FT_EVENT_MODEM_STATUS,FT_EVENT_LINE_STATUS)
  status = ccall((:FT_SetEventNotification, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, DWORD,       Ptr{Cvoid}),
                  ftHandle,  dwEventMask, pvArg)
  check(status)
  return
end

"""
    FT_SetChars(ftHandle::FT_HANDLE, uEventCh, uEventChEn, uErrorCh, uErrorChEn)

This function sets the special characters for the device.

This function allows for inserting specified characters in the data stream to represent
events firing or errors occurring.
"""
function FT_SetChars(ftHandle::FT_HANDLE, uEventCh, uEventChEn, uErrorCh, uErrorChEn)
  status = ccall((:FT_SetChars, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, UCHAR,    UCHAR,      UCHAR,    UCHAR,),
                  ftHandle,  uEventCh, uEventChEn, uErrorCh, uErrorChEn)
  check(status)
  return
end

"""
    FT_SetBreakOn(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetBreakOn(handle) # break now on...

julia> FT_Close(handle)
```
"""
function FT_SetBreakOn(ftHandle::FT_HANDLE)
  status = ccall((:FT_SetBreakOn, libftd2xx), cdecl, FT_STATUS, (FT_HANDLE,),
                                                           ftHandle)
  check(status)
  return
end

"""
    FT_SetBreakOff(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetBreakOff(handle) # break now off...

julia> FT_Close(handle)
```
"""
function FT_SetBreakOff(ftHandle::FT_HANDLE)
  status = ccall((:FT_SetBreakOff, libftd2xx), cdecl, FT_STATUS, (FT_HANDLE,),
                                                           ftHandle)
  check(status)
  return
end

"""
    FT_Purge(ftHandle::FT_HANDLE, dwMask)

# Arguments
 - `ftHandle::FT_HANDLE` : handle to open device
 - `dwMask` : must be `FT_PURGE_RX`, `FT_PURGE_TX` or
   `FT_PURGE_TX | FT_PURGE_RX`.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_Purge(handle, FT_PURGE_RX|FT_PURGE_TX)

julia> nbrx, nbtx, eventstatus = FT_GetStatus(handle) # All queues empty!
(0x00000000, 0x00000000, 0x00000000)

julia> FT_Close(handle)
```
"""
function FT_Purge(ftHandle::FT_HANDLE, dwMask)
  @assert (dwMask == FT_PURGE_RX) || (dwMask == FT_PURGE_TX) ||
          (dwMask == FT_PURGE_RX|FT_PURGE_TX)
  status = ccall((:FT_SetBreakOff, libftd2xx), cdecl, FT_STATUS, (FT_HANDLE, DWORD),
                                                           ftHandle,   dwMask)
  check(status)
  return
end

"""
    FT_ResetDevice(ftHandle::FT_HANDLE)

Send a reset command to the device.

# Example
"""
function FT_ResetDevice(ftHandle::FT_HANDLE)
  status = ccall((:FT_ResetDevice, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE,),
                  ftHandle)
  check(status)
  return
end

"""
    FT_StopInTask(ftHandle::FT_HANDLE)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_StopInTask(handle) # The driver's IN task is now stopped.

julia> FT_RestartInTask(handle) # The driver's IN task is now restarted.

julia> FT_Close(handle)
```
"""
function FT_StopInTask(ftHandle::FT_HANDLE)
  status = ccall((:FT_StopInTask, libftd2xx), cdecl, FT_STATUS, (FT_HANDLE,),
                                                           ftHandle)
  check(status)
  return
end

"""
    FT_RestartInTask(ftHandle::FT_HANDLE)

# Example

See `FT_StopInTask`.
"""
function FT_RestartInTask(ftHandle::FT_HANDLE)
  status = ccall((:FT_RestartInTask, libftd2xx), cdecl, FT_STATUS, (FT_HANDLE,),
                                                           ftHandle)
  check(status)
  return
end

"""
    FT_GetLatencyTimer(ftHandle::FT_HANDLE)

Get the current latency timer in milliseconds.

# Example

See `FT_GetLatencyTimer`.
"""
function FT_GetLatencyTimer(ftHandle::FT_HANDLE)
  pucTimer = Ref{UCHAR}()
  status = ccall((:FT_GetLatencyTimer, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, Ref{UCHAR}),
                  ftHandle,  pucTimer)
  check(status)
  pucTimer[]
end

"""
    FT_SetLatencyTimer(ftHandle::FT_HANDLE, ucTimer)

Get the current latency timer value in milliseconds.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetLatencyTimer(handle, 0x0A)

julia> FT_GetLatencyTimer(handle)
0x0a

See `FT_GetLatencyTimer`.
"""
function FT_SetLatencyTimer(ftHandle::FT_HANDLE, ucTimer)
  status = ccall((:FT_SetLatencyTimer, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, UCHAR),
                  ftHandle,  ucTimer)
  check(status)
  return
end

"""
    FT_GetBitMode(ftHandle::FT_HANDLE)

Get the current bit mode.

# Example

See `FT_SetBitMode`.
"""
function FT_GetBitMode(ftHandle::FT_HANDLE)
  pucMode = Ref{UCHAR}()
  status = ccall((:FT_GetBitMode, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, Ref{UCHAR}),
                  ftHandle,  pucMode)
  check(status)
  pucMode[]
end

"""
    FT_SetBitMode(ftHandle::FT_HANDLE, ucMask, ucMode)

Enables different chip modes.

`ucMask` sets up which bits are inputs and outputs. A bit value of 0 sets the corresponding
pin to an input, a bit value of 1 sets the corresponding pin to an output. In the case of
CBUS Bit Bang, the upper nibble of this value controls which pins are inputs and outputs,
while the lower nibble controls which of the outputs are high and low.

`ucModeMode` sets the chip mode, and must be one of the following:
0x0 = Reset
0x1 = Asynchronous Bit Bang
0x2 = MPSSE (FT2232, FT2232H, FT4232H and FT232H devices only)
0x4 = Synchronous Bit Bang (FT232R, FT245R,FT2232, FT2232H, FT4232H and FT232H devices only)
0x8 = MCU Host Bus Emulation Mode (FT2232, FT2232H, FT4232H and FT232H devices only)
0x10 = FastOpto-Isolated Serial Mode (FT2232, FT2232H, FT4232H and FT232H devices only)
0x20 = CBUS Bit Bang Mode (FT232Rand FT232H devices only)
0x40 = Single Channel Synchronous 245 FIFO Mode (FT2232H and FT232H devices only)

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetBitMode(handle, 0x0F, FT_MODE_ASYNC_BITBANG)

julia> FT_GetBitMode(handle)
0x01

See [`FT_GetBitMode`](@ref).
"""
function FT_SetBitMode(ftHandle::FT_HANDLE, ucMask, ucMode)
  @assert ucMode in (FT_MODE_RESET,FT_MODE_ASYNC_BITBANG,FT_MODE_MPSSE,FT_MODE_SYNC_BITBANG,FT_MODE_MCU_EMULATION,FT_MODE_FAST_OPTO,FT_MODE_CBUS_BITBANG,FT_MODE_SCS_FIFO)
  status = ccall((:FT_SetBitMode, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, UCHAR,  UCHAR),
                  ftHandle,  ucMask, ucMode)
  check(status)
  return
end

"""
    FT_SetUSBParameters(ftHandle::FT_HANDLE, dwInTransferSize, dwOutTransferSize)

Set the USB request transfer size.

This function can be used to change the transfer sizes from the default transfer size
of 4096 bytes to better suit the application requirements. Transfer sizes must be set to a
multiple of 64 bytes between 64 bytes and 64k bytes. When FT_SetUSBParameters is called,
the change comes into effect immediately and any data that was held in the driver at the
time of the change is lost. Note that, at present, only dwInTransferSize is supported.

# Example

```julia-repl
julia> numdevs = FT_CreateDeviceInfoList()
0x00000004

julia> handle = FT_Open(0)
FT_HANDLE(Ptr{Nothing} @0x00000000051e56c0)

julia> FT_SetUSBParameters(handle, 16384, 8192)
"""
function FT_SetUSBParameters(ftHandle::FT_HANDLE, dwInTransferSize, dwOutTransferSize)
  @assert 64 <= dwInTransferSize <= 65536
  @assert 64 <= dwOutTransferSize <= 65536
  status = ccall((:FT_SetUSBParameters, libftd2xx), cdecl, FT_STATUS,
                 (FT_HANDLE, DWORD,            DWORD),
                  ftHandle,  dwInTransferSize, dwOutTransferSize)
  check(status)
  return
end

end # module Wrapper


text:
@testset "RelFinField" begin

  @testset "Basic properties" begin
    F = FiniteField(3, 3, cached = false)[1]
    x = PolynomialRing(F, "x", cached = false)[2]
    K, gK = @inferred FiniteField(x^2+1, "a")
    dp = @inferred defining_polynomial(K)
    @test dp == x^2+1
    @test degree(K) == 2
    bK = @inferred base_field(K)
    @test bK == F
    o = @inferred order(K)
    @test o == 27^2
    @test absolute_degree(K) == 6
  end

  @testset "Promote rule" begin
    F, gF = FiniteField(3, 3, cached = false)
    x = PolynomialRing(F, "x", cached = false)[2]
    K, gK = FiniteField(x^2+1, "a")
    Kt, t = K["t"]
    L, gL = @inferred FiniteField(t^5+t^4+t^2+1, "b")
    dp = @inferred defining_polynomial(L)
    el = @inferred gL + gK
    @test parent(el) == L
    el1 = @inferred gL + gK
    @test parent(el1) == L
    @test iszero(dp(gL))
  end

  @testset "Basic operations" begin
    F, gF = FiniteField(3, 3, cached = false)
    x = PolynomialRing(F, "x", cached = false)[2]
    K, gK = FiniteField(x^2+1, "a")
    Kt, t = K["t"]
    L, gL = FiniteField(t^5+t^4+t^2+1, "b")
    @inferred gL + gL
    @inferred gL - gL
    @inferred gL*gL
    el = @inferred inv(gL)
    @test el == -(gL^4+gL^3+gL)
    @inferred div(gL, gL)
    @test iszero(gL + gL + gL)
    @test iszero(3*gL)
    @test gL^3 == gL*gL*gL
    @test gL^5 == 2*(gL^4+gL^2+1)
    @test isone(-gL^5-gL^4-gL^2)
  end

  @testset "Norm, Trace, Minpoly" begin
    F, gF = FiniteField(3, 3, cached = false)
    x = PolynomialRing(F, "x", cached = false)[2]
    K, gK = FiniteField(x^2+1, "a")
    Kt, t = K["t"]
    L, gL = FiniteField(t^5+t^4+t^2+1, "b")
    el = @inferred absolute_norm(gL)
    @test isone(el)
    el1 = @inferred absolute_tr(gL)
    @test iszero(el1)
    @test isone(-tr(gL))
    @test isone(norm(gL))
    f = @inferred Hecke.absolute_minpoly(gL)
    Fx = parent(f)
    x = gen(Fx)
    @test f == x^5+x^4+x^2+1
    g = @inferred minpoly(gL+1)
    @test iszero(g(gL+1))
    Rx = parent(g)
    y = gen(Rx)
    @test g(y+1) == y^5+y^4+y^2+1
  end

  @testset "Absolute basis and coordinates" begin
    F, gF = FiniteField(3, 3, cached = false)
    x = PolynomialRing(F, "x", cached = false)[2]
    K, gK = FiniteField(x^2+1, "a")
    Kt, t = K["t"]
    L, gL = FiniteField(t^5+t^4+t^2+1, "b")
    B = absolute_basis(L)
    for i = 1:length(B)
      v = absolute_coordinates(B[i])
      for j = 1:length(v)
        if i == j
          @test isone(v[j])
        else
          @test iszero(v[j])
        end
      end
    end
  end

  @testset "Polynomials" begin
    F, gF = FiniteField(3, 3, cached = false)
    x = PolynomialRing(F, "x", cached = false)[2]
    K, gK = FiniteField(x^2+1, "a")
    Kt, t = K["t"]
    L, gL = FiniteField(t^5+t^4+t^2+1, "b")
    Ly, y = L["y"]
    @test isirreducible(y^13+2*y+1)
  end
end

ext: jl


text:
mutable struct ProjMPO_MPS
  PH::ProjMPO
  pm::Vector{ProjMPS}
  weight::Float64
  nsite::Int # default value = 2
  ProjMPO_MPS(PH::ProjMPO, pm::Vector{ProjMPS}, weight::Float64, nsite::Int) = new(PH, pm, weight, nsite)
  ProjMPO_MPS(PH::ProjMPO, pm::Vector{ProjMPS}, weight::Float64) = new(PH, pm, weight, 2)
end

function ProjMPO_MPS(H::MPO,mpsv::Vector{MPS};kwargs...)
  weight = get(kwargs, :weight, 1.0)
  nsite = get(kwargs, :nsite, 2)
  return ProjMPO_MPS(ProjMPO(H, nsite), [ProjMPS(nsite, m) for m in mpsv], weight, nsite)
end

ProjMPO_MPS(H::MPO,Ms::MPS...;weight=1.0) = ProjMPO_MPS(H,[Ms...],weight)

nsite(P::ProjMPO_MPS) = nsite(P.PH)

Base.length(P::ProjMPO_MPS) = length(P.PH)

function product(P::ProjMPO_MPS,
                 v::ITensor)::ITensor
  Pv = product(P.PH,v)
  for p in P.pm
    Pv += P.weight*product(p,v)
  end
  return Pv
end

function Base.eltype(P::ProjMPO_MPS)
  elT = eltype(P.PH)
  for p in P.pm
    elT = promote_type(elT,eltype(p))
  end
  return elT
end

(P::ProjMPO_MPS)(v::ITensor) = product(P,v)

Base.size(P::ProjMPO_MPS) = size(P.H)

function position!(P::ProjMPO_MPS,psi::MPS,pos::Int)
  position!(P.PH,psi,pos)
  for p in P.pm
    position!(p,psi,pos)
  end
end

noiseterm(P::ProjMPO_MPS,
          phi::ITensor,
          dir::String) = noiseterm(P.PH,phi,dir)


text:
# Defines a differential equation describing changes in concentration with
# respect to time due to advection.

macro advect(Δ, bc)
    return :(
        function advect!(du,u,p,t)
            du = -p[1]*(Δ*bc*u)
        end
    )
  end

max_stars_repo_name: elizabethjmohr/hyporheicBGC

max_issues_repo_name: elizabethjmohr/hyporheicBGC

max_forks_repo_name: elizabethjmohr/hyporheicBGC


text:
"""
$(TYPEDEF)
"""
struct EmptySurrealSet <: SurrealSet{Union{}} end

const ∅ = EmptySurrealSet()

Base.length(::EmptySurrealSet) = 0
Base.IteratorSize(::Type{EmptySurrealSet}) = Base.HasLength()

Base.iterate(::EmptySurrealSet) = nothing

Base.:-(::EmptySurrealSet) = EmptySurrealSet()

Base.show(io::IO, ::EmptySurrealSet) = print(io, "∅")

"""
$(TYPEDEF)

A set containing a single surreal number.

Get the number using `only(s)`.
"""
struct Singleton{T} <: SurrealSet{T}
	x::T

	Singleton(x) = new{typeof(x)}(x)
end

Base.length(::Singleton) = 1
Base.IteratorSize(::Type{<:Singleton}) = Base.HasLength()

Base.iterate(s::Singleton) = (s.x, nothing)
Base.iterate(::Singleton, ::Nothing) = nothing

Base.only(s::Singleton) = s.x

"""
$(TYPEDEF)

Wrapper around an arbitrary collection of surreal numbers.
"""
struct WrappedSurrealSet{T, S} <: SurrealSet{T}
	s::S

	WrappedSurrealSet(s) = new{eltype(s), typeof(s)}(s)
end

Base.length(s::WrappedSurrealSet) = length(s.s)
Base.IteratorSize(::Type{WrappedSurrealSet{T, S}}) where {T, S} = Base.IteratorSize(S)

Base.iterate(s::WrappedSurrealSet, args...) = iterate(s.s, args...)


text:
@testset "simple models" begin
    y  = [0,1,0,1,1,0]
    x0 = ones(length(y))
    x1 = [2.5,1.7,4,2.3,3.7,4.8]
    x2 = [3,3,2,2,4,1]
    x3 = [0,0,0,0,1,1]
    X_0 = x0
    X_1 = hcat(x0, x1)
    X_2 = hcat(X_1, x2)
    X_3 = hcat(X_2, x3)

    @testset "model X_0" begin
        model_0 = logreg(y, X_0)

        @test model_0.num_obs == 6
        @test model_0.dof_log == 0
        @test model_0.dof_resid == 5
        @test model_0.dof_total == 5
        @test model_0.beta_hat ≈ [0.0000] atol = 1e-5
        @test model_0.llk ≈ -4.158883 atol = 1e-5
        @test model_0.dev_residuals ≈ [-1.17741, 1.17741, -1.17741, 1.17741,  1.17741, -1.17741] atol = 1e-5
        @test model_0.aic ≈ 10.318 atol = 1e-3
        @test model_0.bic ≈ 10.10953 atol = 1e-4
        @test model_0.pi_hat ≈ [0.5, 0.5, 0.5, 0.5, 0.5, 0.5] atol = 1e-2
        @test model_0.y_hat == ones(Int64, 6)
        @test model_0.std_error ≈ [0.8165] atol = 1e-3
    end

    @testset "model X_1" begin
        model_1 = logreg(y, X_1)
        @test model_1.num_obs == 6
        @test model_1.beta_hat ≈ [3.971,-1.260]  atol = 1e-3
        @test model_1.dof_log == 1
        @test model_1.dof_resid == 4
        @test model_1.dof_total == 5
        @test model_1.pi_hat ≈ [0.6940996, 0.8614932, 0.2551409, 0.7448733, 0.3333144, 0.1110785] atol = 1e-3
        @test model_1.y_hat ≈ [1, 1 , 0, 1, 0, 0] atol = 1e-3
        @test model_1.llk ≈ -3.1391 atol = 1e-3
        @test model_1.aic ≈  10.2782 atol = 1e-3
        @test model_1.bic ≈ 9.86172 atol = 1e-3
        @test model_1.dev_residuals ≈  [-1.5391528,  0.5460551, -0.7675418,  0.7675169,  1.4823421, -0.4852760] atol = 1e-3
        @test model_1.std_error ≈ [3.353, 1.026] atol = 1e-3
    end

    @testset "model X_2" begin
        model_2 = logreg(y, X_2)

        @test model_2.num_obs == 6
        @test model_2.dof_log == 2
        @test model_2.dof_resid == 3
        @test model_2.dof_total == 5
        @test model_2.beta_hat ≈ [0.7187, -1.2929, 1.2082] atol = 1e-4
        @test model_2.llk ≈ -2.639604 atol = 1e-5
        @test model_2.dev_residuals ≈ [-1.6707271, 0.4704612, -0.4952902, 1.1096542, 0.8731406, -0.1659078] atol = 1e-7
        @test model_2.aic ≈ 11.279 atol = 1e-3
        @test model_2.bic ≈ 10.65449 atol = 1e-4
        @test model_2.pi_hat ≈ [0.75233160, 0.89523692, 0.11543226, 0.54028085, 0.68304996, 0.01366843] atol = 1e-7
        @test model_2.y_hat == [1, 1, 0, 1, 1, 0]
        @test model_2.std_error ≈ [4.8242, 1.3811, 1.3498] atol = 1e-3
    end
end


text:
"""
    get_buff_modifier(buff)

Compute the mulitplier associated with stat buffs (multiplied by 12 to return
an integer). As a result, the multiplier for no buff effect is 0. Inputs should
be between -4 and 4.

# Examples
```jldoctest
julia> get_buff_modifier(Int8(0))
12
"""
function get_buff_modifier(a::UInt8, d::UInt8)
    a1, a2 = a > UInt8(3) ? (a, UInt8(4)) : (UInt8(4), UInt8(8) - a)
    d1, d2 = d > UInt8(3) ? (d, UInt8(4)) : (UInt8(4), UInt8(8) - d)
    return a1 * d2, a2 * d1
end

struct EvaluateFastMovesOutput
    attacker_energy::UInt8
    defender_hp::UInt16
end

"""
    evaluate_fast_moves(state, static_state, agent)

Takes in the dynamic state, the static state, and the attacking agent and
returns the dynamic state after the fast move has occurred, with precisely one
copy
"""
function evaluate_fast_move(static_state::StaticState, agent::UInt8,
    attacker_active::UInt8, attacker_energy::UInt8,
    defender_hp::UInt16, fm_dmg::UInt16)

    attacker_energy +=
        get_energy(static_state[agent][attacker_active].fast_move)
    defender_hp -= min(defender_hp, fm_dmg)

    return EvaluateFastMovesOutput(attacker_energy, defender_hp)
end

struct ApplyBuffOutput
    a1::UInt8
    d1::UInt8
    a2::UInt8
    d2::UInt8
end

apply_buff(a1::UInt8, d1::UInt8, a2::UInt8, d2::UInt8, move::ChargedMove) =
    ApplyBuffOutput(
        clamp(iszero(get_buff_target(move)) ? a1 + get_atk(move.buff) : a1,
            0x00, 0x09),
        clamp(iszero(get_buff_target(move)) ? d1 + get_def(move.buff) : d1,
            0x00, 0x09),
        clamp(!iszero(get_buff_target(move)) ? a2 + get_atk(move.buff) : a2,
            0x00, 0x09),
        clamp(!iszero(get_buff_target(move)) ? d2 + get_def(move.buff) : d2,
        0x00, 0x09)
    )

struct EvaluateChargedMovesOutput
    chance::UInt8
    a1::UInt8
    d1::UInt8
    a2::UInt8
    d2::UInt8
    fm_dmg_1::UInt16
    fm_dmg_2::UInt16
    attacker_energy::UInt8
    defender_hp::UInt16
    shields::UInt8
    cmp::UInt8
end

"""
    evaluate_charged_move(state, static_state, cmp, move_id, charge, shielding,
        buffs_applied)

Takes in the dynamic state, the static state, the attacking agent, the move,
the charge, whether or not the opponent shields, and whether or not buffs are
applied (say in the case of a random buff move) and returns
the dynamic state after the charged move has occurred, with precisely one copy
"""
function evaluate_charged_move(static_state::StaticState, cmp::UInt8,
    move_id::UInt8, shielding::Bool, active_1::UInt8, active_2::UInt8,
    a1::UInt8, d1::UInt8, a2::UInt8, d2::UInt8, attacker_energy::UInt8,
    defender_hp::UInt16, shields::UInt8, fm_dmg_1::UInt16, fm_dmg_2::UInt16)

    a_active, d_active, agent, d_agent = isodd(cmp) ?
        (active_1, active_2, 0x01, 0x02) : (active_2, active_1, 0x02, 0x01)
    move = move_id == 0x01 ? static_state[agent][a_active].charged_move_1 :
        static_state[agent][a_active].charged_move_2

    chance = 0x00
    buff_chance = get_buff_chance(move)
    if buff_chance == 1.0
        buff_output = apply_buff(a1, d1, a2, d2, move)
        a1 = buff_output.a1
        d1 = buff_output.d1
        a2 = buff_output.a2
        d2 = buff_output.d2
        fm_dmg_1 = calculate_damage(static_state[0x01][active_1].stats.attack,
            static_state[0x02][active_2].stats.defense,
            (static_state[0x02][active_2].primary_type,
                static_state[0x02][active_2].secondary_type),
            a1, d2, static_state[0x01][active_1].fast_move)
        fm_dmg_2 = calculate_damage(static_state[0x02][active_2].stats.attack,
            static_state[0x01][active_1].stats.defense,
            (static_state[0x01][active_1].primary_type,
                static_state[0x01][active_1].secondary_type),
            a2, d1, static_state[0x02][active_2].fast_move)
    elseif !iszero(buff_chance)
        chance = agent == 0x01 ? (move_id == 0x01 ? 0x01 : 0x02) :
            (move_id == 0x01 ? 0x03 : 0x04)
    end

    attacker_energy -= get_energy(move)
    defender_hp -= shielding ? 0x0001 : min(defender_hp,
        calculate_damage(static_state[agent][a_active].stats.attack,
        static_state[d_agent][d_active].stats.defense,
        (static_state[d_agent][d_active].primary_type,
            static_state[d_agent][d_active].secondary_type),
        agent == 0x01 ? a1 : a2, agent == 0x02 ? d1 : d2,
        move_id == 0x01 ? static_state[agent][a_active].charged_move_1 :
            static_state[agent][a_active].charged_move_2))
    shields -= shielding ? 0x01 : 0x00
    cmp = defender_hp == 0x0000 || cmp < 0x03 ? 0x00 : cmp == 0x04 ? 0x01 : 0x02

    return EvaluateChargedMovesOutput(chance, a1, d1, a2, d2, fm_dmg_1,
        fm_dmg_2, attacker_energy, defender_hp, shields, cmp)
end

struct EvaluateSwitchOutput
    active::UInt8
    switch_cooldown_1::UInt8
    switch_cooldown_2::UInt8
    fm_dmg_1::UInt16
    fm_dmg_2::UInt16
end

"""
    evaluate_switch(state, static_state, agent, to_switch, time)

Takes in the dynamic state, the switching agent, which team member they switch
to, and the time in the switch (only applies in switches after a faint) and
returns the dynamic state after the switch has occurred, with precisely one copy
"""
function evaluate_switch(static_state::StaticState, agent::UInt8,
    to_switch::UInt8, time::UInt8, active_1::UInt8, active_2::UInt8,
    switch_cooldown_1::UInt8, switch_cooldown_2::UInt8)

    active_1 = agent == 0x01 ?
        (active_1 == 0x01 ? to_switch == 0x01 ? 0x02 : 0x03  :
         active_1 == 0x02 ? to_switch == 0x01 ? 0x01 : 0x03  :
                            to_switch == 0x01 ? 0x01 : 0x02) : active_1
    active_2 = agent == 0x02 ?
        (active_2 == 0x01 ? to_switch == 0x01 ? 0x02 : 0x03  :
         active_2 == 0x02 ? to_switch == 0x01 ? 0x01 : 0x03  :
                            to_switch == 0x01 ? 0x01 : 0x02) : active_2

    switch_cooldown_1 = (agent == 0x01 && time == 0x00) ? 0x78 :
        switch_cooldown_1 - min(switch_cooldown_1, time)
    switch_cooldown_2 = (agent == 0x02 && time == 0x00) ? 0x78 :
        switch_cooldown_2 - min(switch_cooldown_2, time)

    fm_dmg_1 = calculate_damage(static_state[0x01][active_1].stats.attack,
        static_state[0x02][active_2].stats.defense,
        (static_state[0x02][active_2].primary_type,
            static_state[0x02][active_2].secondary_type),
        0x04, 0x04, static_state[0x01][active_1].fast_move)
    fm_dmg_2 = calculate_damage(static_state[0x02][active_2].stats.attack,
        static_state[0x01][active_1].stats.defense,
        (static_state[0x01][active_1].primary_type,
            static_state[0x01][active_1].secondary_type),
        0x04, 0x04, static_state[0x02][active_2].fast_move)

    return EvaluateSwitchOutput(agent == 0x01 ? active_1 : active_2,
        switch_cooldown_1, switch_cooldown_2, fm_dmg_1, fm_dmg_2)
end

struct StepTimersOutput
    fm_pending_1::UInt8
    fm_pending_2::UInt8
    switch_cooldown_1::UInt8
    switch_cooldown_2::UInt8
end

"""
    step_timers(state, fmCooldown1, fmCooldown2)

Given the dynamic state and the fast move cooldowns, adjust the times so that
one turn has elapsed, and reset fast move cooldowns as needed. This returns a
new DynamicState using precisely one copy
"""
function step_timers(fm_cooldown_1::UInt8, fm_cooldown_2::UInt8,
    fm_pending_1::UInt8, fm_pending_2::UInt8,
    switch_cooldown_1::UInt8, switch_cooldown_2::UInt8)

    fm_pending_1 = !iszero(fm_cooldown_1) ? fm_cooldown_1 :
                   !iszero(fm_pending_1)  ? fm_pending_1 - 0x01 : 0x00
    fm_pending_2 = !iszero(fm_cooldown_2) ? fm_cooldown_2 :
                   !iszero(fm_pending_2)  ? fm_pending_2 - 0x01 : 0x00

    switch_cooldown_1 = switch_cooldown_1 == 0x00 ? 0x00 :
        switch_cooldown_1 - 0x01
    switch_cooldown_2 = switch_cooldown_2 == 0x00 ? 0x00 :
        switch_cooldown_2 - 0x01

    return StepTimersOutput(
        fm_pending_1, fm_pending_2, switch_cooldown_1, switch_cooldown_2)
end

"""
    min_score(state, static_state)

Given the state and the static state (here just for starting hp values),
compute the PvPoke-like score that would occur if the first agent stopped
attacking altogether. This is currently only used in computing the final score,
but it could be used as strict bounds for α/β pruning, for example.
"""
min_score(s::DynamicState, static_s::StaticState) = 0.5 * mapreduce(x ->
    static_s[0x02][x].stats.hitpoints - get_hp(s[0x02][x]), +, 0x01:0x03) /
    mapreduce(x -> static_s[0x02][x].stats.hitpoints, +, 0x01:0x03)

"""
    max_score(state, static_state)

Given the state and the static state (here just for starting hp values),
compute the PvPoke-like score that would occur if the second agent stopped
attacking altogether. This is currently only used in computing the final score,
but it could be used as strict bounds for α/β pruning, for example.
"""
max_score(s::DynamicState, static_s::StaticState) = 0.5 +
    0.5 * mapreduce(x -> get_hp(s[0x01][x]), +, 0x01:0x03) /
    mapreduce(x -> static_s[0x01][x].stats.hitpoints, +, 0x01:0x03)

"""
    battle_score(state, static_state)

Given the state and the static state (here just for starting hp values),
compute the PvPoke-like score for the battle. Note that this can also be
computed for battles in progress, and thus differs from PvPoke's use cases
"""
battle_score(s::DynamicState, static_s::StaticState) =
    min_score(s, static_s) + max_score(s, static_s) - 0.5


text:
using TableSchema
import TableSchema: read, validate

include("../src/DataPackage.jl")
using DataPackage
import DataPackage: add_resource

# Initialise a resource from a CSV file
# Source: https://opendata.swiss/en/dataset/kennzahlen-der-schweizer-pflegeheime
res = Resource("data/health/hopitaux_ch_2016_Sample_fr.csv", strict=false, name="hopitaux")

# Add this resource to a blank data package
p = Package()
add_resource(p, res)

# Read the data from the table
data = DataPackage.read(res)
println( "The number of cells is ", length(data[:,1]) )
println( "Column 8 is called ", res.schema.fields[8].name )
println( "Sum of column 8 is ", sum([ row for row in data[:,8] ]) )

# Validate the table
table = DataPackage.get_table(res)
if validate(table); println("The table is valid according to the Schema"); end


text:
baremodule BenchPerfConfigSweeps

function load end
# function simpletable end
# function flattable end

module Internal

import BenchPerf
import BenchmarkConfigSweeps
using BenchmarkTools: BenchmarkTools, BenchmarkGroup

# TODO: Turn these to public API?
using BenchPerf.Internal: GroupPerf, GroupResult, TrialPerf, TrialResult
using BenchmarkConfigSweeps.Internal: SweepResult, resultpath

using ..BenchPerfConfigSweeps: BenchPerfConfigSweeps

include("internal.jl")

end  # module Internal

end  # baremodule BenchPerfConfigSweeps


text:
@testset "map_optimize" begin

    # smoke test a function without retval gradient
    @gen function foo()
        x = @trace(normal(0, 1), :x)
        return x
    end

    (trace, _) = generate(foo, ())
    new_trace = map_optimize(trace, select(:x))

    # smoke test a function with retval gradient
    @gen (grad) function foo()
        x = @trace(normal(0, 1), :x)
        return x
    end

    (trace, _) = generate(foo, ())
    new_trace = map_optimize(trace, select(:x))
end